Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/dialects/sqlite/base.py: 32%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

828 statements  

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 

10r''' 

11.. dialect:: sqlite 

12 :name: SQLite 

13 :normal_support: 3.12+ 

14 :best_effort: 3.7.16+ 

15 

16.. _sqlite_datetime: 

17 

18Date and Time Types 

19------------------- 

20 

21SQLite does not have built-in DATE, TIME, or DATETIME types, and pysqlite does 

22not 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 

25and parsing functionality when SQLite is used. The implementation classes are 

26:class:`_sqlite.DATETIME`, :class:`_sqlite.DATE` and :class:`_sqlite.TIME`. 

27These types represent dates and times as ISO formatted strings, which also 

28nicely support ordering. There's no reliance on typical "libc" internals for 

29these functions so historical dates are fully supported. 

30 

31Ensuring Text affinity 

32^^^^^^^^^^^^^^^^^^^^^^ 

33 

34The DDL rendered for these types is the standard ``DATE``, ``TIME`` 

35and ``DATETIME`` indicators. However, custom storage formats can also be 

36applied to these types. When the 

37storage format is detected as containing no alpha characters, the DDL for 

38these types is rendered as ``DATE_CHAR``, ``TIME_CHAR``, and ``DATETIME_CHAR``, 

39so 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 

48SQLite Auto Incrementing Behavior 

49---------------------------------- 

50 

51Background on SQLite's autoincrement is at: https://sqlite.org/autoinc.html 

52 

53Key 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 

65Using the AUTOINCREMENT Keyword 

66^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

67 

68To specifically render the AUTOINCREMENT keyword on the primary key column 

69when rendering DDL, add the flag ``sqlite_autoincrement=True`` to the Table 

70construct:: 

71 

72 Table( 

73 "sometable", 

74 metadata, 

75 Column("id", Integer, primary_key=True), 

76 sqlite_autoincrement=True, 

77 ) 

78 

79Allowing autoincrement behavior SQLAlchemy types other than Integer/INTEGER 

80^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

81 

82SQLite's typing model is based on naming conventions. Among other things, this 

83means that any type name which contains the substring ``"INT"`` will be 

84determined to be of "integer affinity". A type named ``"BIGINT"``, 

85``"SPECIAL_INT"`` or even ``"XYZINTQPR"``, will be considered by SQLite to be 

86of "integer" affinity. However, **the SQLite autoincrement feature, whether 

87implicitly or explicitly enabled, requires that the name of the column's type 

88is exactly the string "INTEGER"**. Therefore, if an application uses a type 

89like :class:`.BigInteger` for a primary key, on SQLite this type will need to 

90be rendered as the name ``"INTEGER"`` when emitting the initial ``CREATE 

91TABLE`` statement in order for the autoincrement behavior to be available. 

92 

93One approach to achieve this is to use :class:`.Integer` on SQLite 

94only 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 

106Another is to use a subclass of :class:`.BigInteger` that overrides its DDL 

107name 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 

141Transactions with SQLite and the sqlite3 driver 

142----------------------------------------------- 

143 

144As a file-based database, SQLite's approach to transactions differs from 

145traditional databases in many ways. Additionally, the ``sqlite3`` driver 

146standard with Python (as well as the async version ``aiosqlite`` which builds 

147on top of it) has several quirks, workarounds, and API features in the 

148area of transaction control, all of which generally need to be addressed when 

149constructing a SQLAlchemy application that uses SQLite. 

150 

151Legacy Transaction Mode with the sqlite3 driver 

152^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

153 

154The most important aspect of transaction handling with the sqlite3 driver is 

155that it defaults (which will continue through Python 3.15 before being 

156removed in Python 3.16) to legacy transactional behavior which does 

157not strictly follow :pep:`249`. The way in which the driver diverges from the 

158PEP 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 

160DELETE. Normally, :pep:`249` dictates that a BEGIN must be emitted upon 

161the first SQL statement of any kind, so that all subsequent operations will 

162be established within a transaction until ``connection.commit()`` has been 

163called. The ``sqlite3`` driver, in an effort to be easier to use in 

164highly concurrent environments, skips this step for DQL (e.g. SELECT) statements, 

165and also skips it for DDL (e.g. CREATE TABLE etc.) statements for more legacy 

166reasons. Statements such as SAVEPOINT are also skipped. 

167 

168In modern versions of the ``sqlite3`` driver as of Python 3.12, this legacy 

169mode 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 

171effect by default due to the ``Connection.autocommit`` parameter being set to 

172the constant ``sqlite3.LEGACY_TRANSACTION_CONTROL``. Prior to Python 3.12, 

173the ``Connection.autocommit`` attribute did not exist. 

174 

175The 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 

197Legacy transaction mode first existed in order to facilitate working around 

198SQLite's file locks. Because SQLite relies upon whole-file locks, it is easy to 

199get "database is locked" errors, particularly when newer features like "write 

200ahead logging" are disabled. This is a key reason why ``sqlite3``'s legacy 

201transaction mode is still the default mode of operation; disabling it will 

202produce behavior that is more susceptible to locked database errors. However 

203note that **legacy transaction mode will no longer be the default** in a future 

204Python version (3.16 as of this writing). 

205 

206.. _sqlite_enabling_transactions: 

207 

208Enabling Non-Legacy SQLite Transactional Modes with the sqlite3 or aiosqlite driver 

209^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

210 

211Current 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 

214the attribute is not available, using event hooks to control the behavior of 

215BEGIN. 

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 

299Using SQLAlchemy's Driver Level AUTOCOMMIT Feature with SQLite 

300^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

301 

302SQLAlchemy has a comprehensive database isolation feature with optional 

303autocommit support that is introduced in the section :ref:`dbapi_autocommit`. 

304 

305For the ``sqlite3`` and ``aiosqlite`` drivers, SQLAlchemy only includes 

306built-in support for "AUTOCOMMIT". Note that this mode is currently incompatible 

307with the non-legacy isolation mode hooks documented in the previous 

308section at :ref:`sqlite_enabling_transactions`. 

309 

310To use the ``sqlite3`` driver with SQLAlchemy driver-level autocommit, 

311create an engine setting the :paramref:`_sa.create_engine.isolation_level` 

312parameter to "AUTOCOMMIT":: 

313 

314 eng = create_engine("sqlite:///myfile.db", isolation_level="AUTOCOMMIT") 

315 

316When using the above mode, any event hooks that set the sqlite3 ``Connection.autocommit`` 

317parameter away from its default of ``sqlite3.LEGACY_TRANSACTION_CONTROL`` 

318as well as hooks that emit ``BEGIN`` should be disabled. 

319 

320Additional Reading for SQLite / sqlite3 transaction control 

321^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

322 

323Links with important information on SQLite, the sqlite3 driver, 

324as 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 

333INSERT/UPDATE/DELETE...RETURNING 

334--------------------------------- 

335 

336The SQLite dialect supports SQLite 3.35's ``INSERT|UPDATE|DELETE..RETURNING`` 

337syntax. ``INSERT..RETURNING`` may be used 

338automatically in some cases in order to fetch newly generated identifiers in 

339place of the traditional approach of using ``cursor.lastrowid``, however 

340``cursor.lastrowid`` is currently still preferred for simple single-statement 

341cases for its better performance. 

342 

343To 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 

374Foreign Key Support 

375------------------- 

376 

377SQLite supports FOREIGN KEY syntax when emitting CREATE statements for tables, 

378however by default these constraints have no effect on the operation of the 

379table. 

380 

381Constraint 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 

390SQLAlchemy allows for the ``PRAGMA`` statement to be emitted automatically for 

391new 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 

432ON 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 

439SQLite supports a non-standard DDL clause known as ON CONFLICT which can be applied 

440to primary key, unique, check, and not null constraints. In DDL, it is 

441rendered either within the "CONSTRAINT" clause or within the column definition 

442itself depending on the location of the target constraint. To render this 

443clause within DDL, the extension parameter ``sqlite_on_conflict`` can be 

444specified with a string conflict resolution algorithm within the 

445:class:`.PrimaryKeyConstraint`, :class:`.UniqueConstraint`, 

446:class:`.CheckConstraint` objects. Within the :class:`_schema.Column` object, 

447there 

448are individual parameters ``sqlite_on_conflict_not_null``, 

449``sqlite_on_conflict_primary_key``, ``sqlite_on_conflict_unique`` which each 

450correspond to the three types of relevant constraint types that can be 

451indicated 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 

458The ``sqlite_on_conflict`` parameters accept a string argument which is just 

459the resolution name to be chosen, which on SQLite can be one of ROLLBACK, 

460ABORT, FAIL, IGNORE, and REPLACE. For example, to add a UNIQUE constraint 

461that 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 

471The 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 

483When using the :paramref:`_schema.Column.unique` 

484flag to add a UNIQUE constraint 

485to a single column, the ``sqlite_on_conflict_unique`` parameter can 

486be added to the :class:`_schema.Column` as well, which will be added to the 

487UNIQUE 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 

498rendering: 

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 

509To 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 

521this 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 

532Similarly, 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 

545SQLAlchemy renders the PRIMARY KEY constraint separately, so the conflict 

546resolution 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 

557INSERT...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 

564From version 3.24.0 onwards, SQLite supports "upserts" (update or insert) 

565of rows into a table via the ``ON CONFLICT`` clause of the ``INSERT`` 

566statement. A candidate row will only be inserted if that row does not violate 

567any unique or primary key constraints. In the case of a unique constraint violation, a 

568secondary action can occur which can be either "DO UPDATE", indicating that 

569the data in the target row should be updated, or "DO NOTHING", which indicates 

570to silently skip this row. 

571 

572Conflicts are determined using columns that are part of existing unique 

573constraints and indexes. These constraints are identified by stating the 

574columns and conditions that comprise the indexes. 

575 

576SQLAlchemy provides ``ON CONFLICT`` support via the SQLite-specific 

577:func:`_sqlite.insert()` function, which provides 

578the generative methods :meth:`_sqlite.Insert.on_conflict_do_update` 

579and :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 

612Specifying the Target 

613^^^^^^^^^^^^^^^^^^^^^ 

614 

615Both 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 

642The SET Clause 

643^^^^^^^^^^^^^^^ 

644 

645``ON CONFLICT...DO UPDATE`` is used to perform an update of the already 

646existing row, using any combination of new values as well as values 

647from the proposed insertion. These values are specified using the 

648:paramref:`_sqlite.Insert.on_conflict_do_update.set_` parameter. This 

649parameter accepts a dictionary which consists of direct values 

650for 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 

673Updating using the Excluded INSERT Values 

674^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

675 

676In order to refer to the proposed insertion row, the special alias 

677:attr:`~.sqlite.Insert.excluded` is available as an attribute on 

678the :class:`_sqlite.Insert` object; this object creates an "excluded." prefix 

679on a column, that informs the DO UPDATE to update the row with the value that 

680would 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 

697Additional WHERE Criteria 

698^^^^^^^^^^^^^^^^^^^^^^^^^ 

699 

700The :meth:`_sqlite.Insert.on_conflict_do_update` method also accepts 

701a WHERE clause using the :paramref:`_sqlite.Insert.on_conflict_do_update.where` 

702parameter, 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 

721Skipping Rows with DO NOTHING 

722^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

723 

724``ON CONFLICT`` may be used to skip inserting a row entirely 

725if any conflict with a unique constraint occurs; below this is illustrated 

726using 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 

736If ``DO NOTHING`` is used without specifying any columns or constraint, 

737it has the effect of skipping the INSERT for any unique violation which 

738occurs: 

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 

749Specifying Multiple ON CONFLICT Clauses 

750^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

751 

752SQLite accepts more than one ``ON CONFLICT`` clause within a single INSERT 

753statement. The :meth:`_sqlite.Insert.on_conflict_do_update` and 

754:meth:`_sqlite.Insert.on_conflict_do_nothing` methods may therefore be 

755invoked repeatedly against the same construct, and may be combined with each 

756other; 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 

769SQLite tests the clauses in the order given, and applies at most one of them 

770to any particular row, that being the first clause whose conflict target 

771matches the constraint that was violated. 

772 

773Only the last ``ON CONFLICT`` clause of a statement may omit its conflict 

774target, in which case it fires for any unique violation not already captured 

775by a preceding clause. A :meth:`_sqlite.Insert.on_conflict_do_nothing` call 

776that omits 

777:paramref:`_sqlite.Insert.on_conflict_do_nothing.index_elements` must 

778therefore 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 

797Type Reflection 

798--------------- 

799 

800SQLite types are unlike those of most other database backends, in that 

801the string name of the type usually does not correspond to a "type" in a 

802one-to-one fashion. Instead, SQLite links per-column typing behavior 

803to one of five so-called "type affinities" based on a string matching 

804pattern for the type. 

805 

806SQLAlchemy's reflection process, when inspecting types, uses a simple 

807lookup table to link the keywords returned to provided SQLAlchemy types. 

808This lookup table is present within the SQLite dialect as it is for all 

809other dialects. However, the SQLite dialect has a different "fallback" 

810routine for when a particular type name is not located in the lookup map; 

811it instead implements the SQLite "type affinity" scheme located at 

812https://www.sqlite.org/datatype3.html section 2.1. 

813 

814The provided typemap will make direct associations from an exact string 

815name 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 

829When a type name does not match one of the above types, the "type affinity" 

830lookup 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 

844Partial Indexes 

845--------------- 

846 

847A partial index, e.g. one which uses a WHERE clause, can be specified 

848with 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 

857The 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 

866Dotted Column Names 

867------------------- 

868 

869Using table or column names that explicitly have periods in them is 

870**not recommended**. While this is generally a bad idea for relational 

871databases in general, as the dot is a syntactically significant character, 

872the SQLite driver up until version **3.10.0** of SQLite has a bug which 

873requires that SQLAlchemy filter out these dots in result sets. 

874 

875The 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 

904The 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 

913Where above, the driver incorrectly reports the names of the columns 

914including the name of the table, which is entirely inconsistent vs. 

915when the UNION is not present. 

916 

917SQLAlchemy relies upon column names being predictable in how they match 

918to the original statement, so the SQLAlchemy dialect has no choice but 

919to 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 

941Note that above, even though SQLAlchemy filters out the dots, *both 

942names 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 

954Therefore, the workaround applied by SQLAlchemy only impacts 

955:meth:`_engine.CursorResult.keys` and :meth:`.Row.keys()` in the public API. In 

956the very specific case where an application is forced to use column names that 

957contain dots, and the functionality of :meth:`_engine.CursorResult.keys` and 

958:meth:`.Row.keys()` is required to return these dotted names unmodified, 

959the ``sqlite_raw_colnames`` execution option may be provided, either on a 

960per-: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 

971or on a per-:class:`_engine.Engine` basis:: 

972 

973 engine = create_engine( 

974 "sqlite://", execution_options={"sqlite_raw_colnames": True} 

975 ) 

976 

977When using the per-:class:`_engine.Engine` execution option, note that 

978**Core and ORM queries that use UNION may not function properly**. 

979 

980SQLite-specific table options 

981----------------------------- 

982 

983One option for CREATE TABLE is supported directly by the SQLite 

984dialect 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 

997Both options are also reflected, so that a :class:`_schema.Table` which is 

998autoloaded from a database that was created using either keyword will render 

999that keyword again when the table is recreated. The reflected values are 

1000also available directly from 

1001:meth:`_engine.Inspector.get_table_options`. 

1002 

1003.. versionadded:: 2.0.53 Added reflection support for the ``WITHOUT ROWID`` 

1004 and ``STRICT`` table options. 

1005 

1006.. seealso:: 

1007 

1008 `SQLite CREATE TABLE options 

1009 <https://www.sqlite.org/lang_createtable.html>`_ 

1010 

1011.. _sqlite_include_internal: 

1012 

1013Reflecting internal schema tables 

1014---------------------------------- 

1015 

1016Reflection methods that return lists of tables will omit so-called 

1017"SQLite internal schema object" names, which are considered by SQLite 

1018as any object name that is prefixed with ``sqlite_``. An example of 

1019such an object is the ``sqlite_sequence`` table that's generated when 

1020the ``AUTOINCREMENT`` column parameter is used. In order to return 

1021these objects, the parameter ``sqlite_include_internal=True`` may be 

1022passed to methods such as :meth:`_schema.MetaData.reflect` or 

1023:meth:`.Inspector.get_table_names`. 

1024 

1025.. versionadded:: 2.0 Added the ``sqlite_include_internal=True`` parameter. 

1026 Previously, these tables were not ignored by SQLAlchemy reflection 

1027 methods. 

1028 

1029.. note:: 

1030 

1031 The ``sqlite_include_internal`` parameter does not refer to the 

1032 "system" tables that are present in schemas such as ``sqlite_master``. 

1033 

1034.. seealso:: 

1035 

1036 `SQLite Internal Schema Objects <https://www.sqlite.org/fileformat2.html#intschema>`_ - in the SQLite 

1037 documentation. 

1038 

1039''' # noqa 

1040 

1041from __future__ import annotations 

1042 

1043import datetime 

1044import numbers 

1045import re 

1046from typing import Any 

1047from typing import Callable 

1048from typing import Optional 

1049from typing import TYPE_CHECKING 

1050 

1051from .json import JSON 

1052from .json import JSONB 

1053from .json import JSONIndexType 

1054from .json import JSONPathType 

1055from ... import exc 

1056from ... import schema as sa_schema 

1057from ... import sql 

1058from ... import text 

1059from ... import types as sqltypes 

1060from ... import util 

1061from ...engine import default 

1062from ...engine import processors 

1063from ...engine import reflection 

1064from ...engine.reflection import ReflectionDefaults 

1065from ...sql import coercions 

1066from ...sql import compiler 

1067from ...sql import ddl as sa_ddl 

1068from ...sql import elements 

1069from ...sql import roles 

1070from ...sql import schema 

1071from ...types import BLOB # noqa 

1072from ...types import BOOLEAN # noqa 

1073from ...types import CHAR # noqa 

1074from ...types import DECIMAL # noqa 

1075from ...types import FLOAT # noqa 

1076from ...types import INTEGER # noqa 

1077from ...types import NUMERIC # noqa 

1078from ...types import REAL # noqa 

1079from ...types import SMALLINT # noqa 

1080from ...types import TEXT # noqa 

1081from ...types import TIMESTAMP # noqa 

1082from ...types import VARCHAR # noqa 

1083 

1084if TYPE_CHECKING: 

1085 from ...engine.interfaces import DBAPIConnection 

1086 from ...engine.interfaces import Dialect 

1087 from ...engine.interfaces import IsolationLevel 

1088 from ...sql.sqltypes import _JSON_VALUE 

1089 from ...sql.type_api import _BindProcessorType 

1090 from ...sql.type_api import _ResultProcessorType 

1091 

1092 

1093class _SQliteJson(JSON): 

1094 def result_processor(self, dialect, coltype): 

1095 default_processor = super().result_processor(dialect, coltype) 

1096 

1097 def process(value): 

1098 try: 

1099 return default_processor(value) 

1100 except TypeError: 

1101 if isinstance(value, numbers.Number): 

1102 return value 

1103 else: 

1104 raise 

1105 

1106 return process 

1107 

1108 

1109class _DateTimeMixin: 

1110 _reg = None 

1111 _storage_format = None 

1112 

1113 def __init__(self, storage_format=None, regexp=None, **kw): 

1114 super().__init__(**kw) 

1115 if regexp is not None: 

1116 self._reg = re.compile(regexp) 

1117 if storage_format is not None: 

1118 self._storage_format = storage_format 

1119 

1120 @property 

1121 def format_is_text_affinity(self): 

1122 """return True if the storage format will automatically imply 

1123 a TEXT affinity. 

1124 

1125 If the storage format contains no non-numeric characters, 

1126 it will imply a NUMERIC storage format on SQLite; in this case, 

1127 the type will generate its DDL as DATE_CHAR, DATETIME_CHAR, 

1128 TIME_CHAR. 

1129 

1130 """ 

1131 spec = self._storage_format % { 

1132 "year": 0, 

1133 "month": 0, 

1134 "day": 0, 

1135 "hour": 0, 

1136 "minute": 0, 

1137 "second": 0, 

1138 "microsecond": 0, 

1139 } 

1140 return bool(re.search(r"[^0-9]", spec)) 

1141 

1142 def adapt(self, cls, **kw): 

1143 if issubclass(cls, _DateTimeMixin): 

1144 if self._storage_format: 

1145 kw["storage_format"] = self._storage_format 

1146 if self._reg: 

1147 kw["regexp"] = self._reg 

1148 return super().adapt(cls, **kw) 

1149 

1150 def literal_processor(self, dialect): 

1151 bp = self.bind_processor(dialect) 

1152 

1153 def process(value): 

1154 return "'%s'" % bp(value) 

1155 

1156 return process 

1157 

1158 

1159class DATETIME(_DateTimeMixin, sqltypes.DateTime): 

1160 r"""Represent a Python datetime object in SQLite using a string. 

1161 

1162 The default string storage format is:: 

1163 

1164 "%(year)04d-%(month)02d-%(day)02d %(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d" 

1165 

1166 e.g.: 

1167 

1168 .. sourcecode:: text 

1169 

1170 2021-03-15 12:05:57.105542 

1171 

1172 The incoming storage format is by default parsed using the 

1173 Python ``datetime.fromisoformat()`` function. 

1174 

1175 .. versionchanged:: 2.0 ``datetime.fromisoformat()`` is used for default 

1176 datetime string parsing. 

1177 

1178 The storage format can be customized to some degree using the 

1179 ``storage_format`` and ``regexp`` parameters, such as:: 

1180 

1181 import re 

1182 from sqlalchemy.dialects.sqlite import DATETIME 

1183 

1184 dt = DATETIME( 

1185 storage_format=( 

1186 "%(year)04d/%(month)02d/%(day)02d %(hour)02d:%(minute)02d:%(second)02d" 

1187 ), 

1188 regexp=r"(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)", 

1189 ) 

1190 

1191 :param truncate_microseconds: when ``True`` microseconds will be truncated 

1192 from the datetime. Can't be specified together with ``storage_format`` 

1193 or ``regexp``. 

1194 

1195 :param storage_format: format string which will be applied to the dict 

1196 with keys year, month, day, hour, minute, second, and microsecond. 

1197 

1198 :param regexp: regular expression which will be applied to incoming result 

1199 rows, replacing the use of ``datetime.fromisoformat()`` to parse incoming 

1200 strings. If the regexp contains named groups, the resulting match dict is 

1201 applied to the Python datetime() constructor as keyword arguments. 

1202 Otherwise, if positional groups are used, the datetime() constructor 

1203 is called with positional arguments via 

1204 ``*map(int, match_obj.groups(0))``. 

1205 

1206 """ # noqa 

1207 

1208 _storage_format = ( 

1209 "%(year)04d-%(month)02d-%(day)02d " 

1210 "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d" 

1211 ) 

1212 

1213 def __init__(self, *args, **kwargs): 

1214 truncate_microseconds = kwargs.pop("truncate_microseconds", False) 

1215 super().__init__(*args, **kwargs) 

1216 if truncate_microseconds: 

1217 assert "storage_format" not in kwargs, ( 

1218 "You can specify only " 

1219 "one of truncate_microseconds or storage_format." 

1220 ) 

1221 assert "regexp" not in kwargs, ( 

1222 "You can specify only one of " 

1223 "truncate_microseconds or regexp." 

1224 ) 

1225 self._storage_format = ( 

1226 "%(year)04d-%(month)02d-%(day)02d " 

1227 "%(hour)02d:%(minute)02d:%(second)02d" 

1228 ) 

1229 

1230 def bind_processor( 

1231 self, dialect: Dialect 

1232 ) -> Optional[_BindProcessorType[Any]]: 

1233 datetime_datetime = datetime.datetime 

1234 datetime_date = datetime.date 

1235 format_ = self._storage_format 

1236 

1237 def process(value): 

1238 if value is None: 

1239 return None 

1240 elif isinstance(value, datetime_datetime): 

1241 return format_ % { 

1242 "year": value.year, 

1243 "month": value.month, 

1244 "day": value.day, 

1245 "hour": value.hour, 

1246 "minute": value.minute, 

1247 "second": value.second, 

1248 "microsecond": value.microsecond, 

1249 } 

1250 elif isinstance(value, datetime_date): 

1251 return format_ % { 

1252 "year": value.year, 

1253 "month": value.month, 

1254 "day": value.day, 

1255 "hour": 0, 

1256 "minute": 0, 

1257 "second": 0, 

1258 "microsecond": 0, 

1259 } 

1260 else: 

1261 raise TypeError( 

1262 "SQLite DateTime type only accepts Python " 

1263 "datetime and date objects as input." 

1264 ) 

1265 

1266 return process 

1267 

1268 def result_processor( 

1269 self, dialect: Dialect, coltype: object 

1270 ) -> Optional[_ResultProcessorType[Any]]: 

1271 if self._reg: 

1272 return processors.str_to_datetime_processor_factory( 

1273 self._reg, datetime.datetime 

1274 ) 

1275 else: 

1276 return processors.str_to_datetime 

1277 

1278 

1279class DATE(_DateTimeMixin, sqltypes.Date): 

1280 r"""Represent a Python date object in SQLite using a string. 

1281 

1282 The default string storage format is:: 

1283 

1284 "%(year)04d-%(month)02d-%(day)02d" 

1285 

1286 e.g.: 

1287 

1288 .. sourcecode:: text 

1289 

1290 2011-03-15 

1291 

1292 The incoming storage format is by default parsed using the 

1293 Python ``date.fromisoformat()`` function. 

1294 

1295 .. versionchanged:: 2.0 ``date.fromisoformat()`` is used for default 

1296 date string parsing. 

1297 

1298 

1299 The storage format can be customized to some degree using the 

1300 ``storage_format`` and ``regexp`` parameters, such as:: 

1301 

1302 import re 

1303 from sqlalchemy.dialects.sqlite import DATE 

1304 

1305 d = DATE( 

1306 storage_format="%(month)02d/%(day)02d/%(year)04d", 

1307 regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)"), 

1308 ) 

1309 

1310 :param storage_format: format string which will be applied to the 

1311 dict with keys year, month, and day. 

1312 

1313 :param regexp: regular expression which will be applied to 

1314 incoming result rows, replacing the use of ``date.fromisoformat()`` to 

1315 parse incoming strings. If the regexp contains named groups, the resulting 

1316 match dict is applied to the Python date() constructor as keyword 

1317 arguments. Otherwise, if positional groups are used, the date() 

1318 constructor is called with positional arguments via 

1319 ``*map(int, match_obj.groups(0))``. 

1320 

1321 """ 

1322 

1323 _storage_format = "%(year)04d-%(month)02d-%(day)02d" 

1324 

1325 def bind_processor( 

1326 self, dialect: Dialect 

1327 ) -> Optional[_BindProcessorType[Any]]: 

1328 datetime_date = datetime.date 

1329 format_ = self._storage_format 

1330 

1331 def process(value): 

1332 if value is None: 

1333 return None 

1334 elif isinstance(value, datetime_date): 

1335 return format_ % { 

1336 "year": value.year, 

1337 "month": value.month, 

1338 "day": value.day, 

1339 } 

1340 else: 

1341 raise TypeError( 

1342 "SQLite Date type only accepts Python " 

1343 "date objects as input." 

1344 ) 

1345 

1346 return process 

1347 

1348 def result_processor( 

1349 self, dialect: Dialect, coltype: object 

1350 ) -> Optional[_ResultProcessorType[Any]]: 

1351 if self._reg: 

1352 return processors.str_to_datetime_processor_factory( 

1353 self._reg, datetime.date 

1354 ) 

1355 else: 

1356 return processors.str_to_date 

1357 

1358 

1359class TIME(_DateTimeMixin, sqltypes.Time): 

1360 r"""Represent a Python time object in SQLite using a string. 

1361 

1362 The default string storage format is:: 

1363 

1364 "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d" 

1365 

1366 e.g.: 

1367 

1368 .. sourcecode:: text 

1369 

1370 12:05:57.10558 

1371 

1372 The incoming storage format is by default parsed using the 

1373 Python ``time.fromisoformat()`` function. 

1374 

1375 .. versionchanged:: 2.0 ``time.fromisoformat()`` is used for default 

1376 time string parsing. 

1377 

1378 The storage format can be customized to some degree using the 

1379 ``storage_format`` and ``regexp`` parameters, such as:: 

1380 

1381 import re 

1382 from sqlalchemy.dialects.sqlite import TIME 

1383 

1384 t = TIME( 

1385 storage_format="%(hour)02d-%(minute)02d-%(second)02d-%(microsecond)06d", 

1386 regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?"), 

1387 ) 

1388 

1389 :param truncate_microseconds: when ``True`` microseconds will be truncated 

1390 from the time. Can't be specified together with ``storage_format`` 

1391 or ``regexp``. 

1392 

1393 :param storage_format: format string which will be applied to the dict 

1394 with keys hour, minute, second, and microsecond. 

1395 

1396 :param regexp: regular expression which will be applied to incoming result 

1397 rows, replacing the use of ``datetime.fromisoformat()`` to parse incoming 

1398 strings. If the regexp contains named groups, the resulting match dict is 

1399 applied to the Python time() constructor as keyword arguments. Otherwise, 

1400 if positional groups are used, the time() constructor is called with 

1401 positional arguments via ``*map(int, match_obj.groups(0))``. 

1402 

1403 """ 

1404 

1405 _storage_format = "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d" 

1406 

1407 def __init__(self, *args, **kwargs): 

1408 truncate_microseconds = kwargs.pop("truncate_microseconds", False) 

1409 super().__init__(*args, **kwargs) 

1410 if truncate_microseconds: 

1411 assert "storage_format" not in kwargs, ( 

1412 "You can specify only " 

1413 "one of truncate_microseconds or storage_format." 

1414 ) 

1415 assert "regexp" not in kwargs, ( 

1416 "You can specify only one of " 

1417 "truncate_microseconds or regexp." 

1418 ) 

1419 self._storage_format = "%(hour)02d:%(minute)02d:%(second)02d" 

1420 

1421 def bind_processor(self, dialect): 

1422 datetime_time = datetime.time 

1423 format_ = self._storage_format 

1424 

1425 def process(value): 

1426 if value is None: 

1427 return None 

1428 elif isinstance(value, datetime_time): 

1429 return format_ % { 

1430 "hour": value.hour, 

1431 "minute": value.minute, 

1432 "second": value.second, 

1433 "microsecond": value.microsecond, 

1434 } 

1435 else: 

1436 raise TypeError( 

1437 "SQLite Time type only accepts Python " 

1438 "time objects as input." 

1439 ) 

1440 

1441 return process 

1442 

1443 def result_processor(self, dialect, coltype): 

1444 if self._reg: 

1445 return processors.str_to_datetime_processor_factory( 

1446 self._reg, datetime.time 

1447 ) 

1448 else: 

1449 return processors.str_to_time 

1450 

1451 

1452colspecs = { 

1453 sqltypes.Date: DATE, 

1454 sqltypes.DateTime: DATETIME, 

1455 sqltypes.JSON: _SQliteJson, 

1456 sqltypes.JSON.JSONIndexType: JSONIndexType, 

1457 sqltypes.JSON.JSONPathType: JSONPathType, 

1458 sqltypes.Time: TIME, 

1459 JSONB: JSONB, 

1460} 

1461 

1462ischema_names = { 

1463 "BIGINT": sqltypes.BIGINT, 

1464 "BLOB": sqltypes.BLOB, 

1465 "BOOL": sqltypes.BOOLEAN, 

1466 "BOOLEAN": sqltypes.BOOLEAN, 

1467 "CHAR": sqltypes.CHAR, 

1468 "DATE": sqltypes.DATE, 

1469 "DATE_CHAR": sqltypes.DATE, 

1470 "DATETIME": sqltypes.DATETIME, 

1471 "DATETIME_CHAR": sqltypes.DATETIME, 

1472 "DOUBLE": sqltypes.DOUBLE, 

1473 "DECIMAL": sqltypes.DECIMAL, 

1474 "FLOAT": sqltypes.FLOAT, 

1475 "INT": sqltypes.INTEGER, 

1476 "INTEGER": sqltypes.INTEGER, 

1477 "JSON": JSON, 

1478 "JSONB": JSONB, 

1479 "NUMERIC": sqltypes.NUMERIC, 

1480 "REAL": sqltypes.REAL, 

1481 "SMALLINT": sqltypes.SMALLINT, 

1482 "TEXT": sqltypes.TEXT, 

1483 "TIME": sqltypes.TIME, 

1484 "TIME_CHAR": sqltypes.TIME, 

1485 "TIMESTAMP": sqltypes.TIMESTAMP, 

1486 "VARCHAR": sqltypes.VARCHAR, 

1487 "NVARCHAR": sqltypes.NVARCHAR, 

1488 "NCHAR": sqltypes.NCHAR, 

1489} 

1490 

1491 

1492class SQLiteCompiler(compiler.SQLCompiler): 

1493 extract_map = util.update_copy( 

1494 compiler.SQLCompiler.extract_map, 

1495 { 

1496 "month": "%m", 

1497 "day": "%d", 

1498 "year": "%Y", 

1499 "second": "%S", 

1500 "hour": "%H", 

1501 "doy": "%j", 

1502 "minute": "%M", 

1503 "epoch": "%s", 

1504 "dow": "%w", 

1505 "week": "%W", 

1506 }, 

1507 ) 

1508 

1509 def visit_truediv_binary(self, binary, operator, **kw): 

1510 return ( 

1511 self.process(binary.left, **kw) 

1512 + " / " 

1513 + "(%s + 0.0)" % self.process(binary.right, **kw) 

1514 ) 

1515 

1516 def visit_now_func(self, fn, **kw): 

1517 return "CURRENT_TIMESTAMP" 

1518 

1519 def visit_localtimestamp_func(self, func, **kw): 

1520 return "DATETIME(CURRENT_TIMESTAMP, 'localtime')" 

1521 

1522 def visit_true(self, expr, **kw): 

1523 return "1" 

1524 

1525 def visit_false(self, expr, **kw): 

1526 return "0" 

1527 

1528 def visit_char_length_func(self, fn, **kw): 

1529 return "length%s" % self.function_argspec(fn) 

1530 

1531 def visit_aggregate_strings_func(self, fn, **kw): 

1532 return super().visit_aggregate_strings_func( 

1533 fn, use_function_name="group_concat", **kw 

1534 ) 

1535 

1536 def visit_cast(self, cast, **kwargs): 

1537 if self.dialect.supports_cast: 

1538 return super().visit_cast(cast, **kwargs) 

1539 else: 

1540 return self.process(cast.clause, **kwargs) 

1541 

1542 def visit_extract(self, extract, **kw): 

1543 try: 

1544 return "CAST(STRFTIME('%s', %s) AS INTEGER)" % ( 

1545 self.extract_map[extract.field], 

1546 self.process(extract.expr, **kw), 

1547 ) 

1548 except KeyError as err: 

1549 raise exc.CompileError( 

1550 "%s is not a valid extract argument." % extract.field 

1551 ) from err 

1552 

1553 def returning_clause( 

1554 self, 

1555 stmt, 

1556 returning_cols, 

1557 *, 

1558 populate_result_map, 

1559 **kw, 

1560 ): 

1561 kw["include_table"] = False 

1562 return super().returning_clause( 

1563 stmt, returning_cols, populate_result_map=populate_result_map, **kw 

1564 ) 

1565 

1566 def limit_clause(self, select, **kw): 

1567 text = "" 

1568 if select._limit_clause is not None: 

1569 text += "\n LIMIT " + self.process(select._limit_clause, **kw) 

1570 if select._offset_clause is not None: 

1571 if select._limit_clause is None: 

1572 text += "\n LIMIT " + self.process(sql.literal(-1)) 

1573 text += " OFFSET " + self.process(select._offset_clause, **kw) 

1574 else: 

1575 text += " OFFSET " + self.process(sql.literal(0), **kw) 

1576 return text 

1577 

1578 def for_update_clause(self, select, **kw): 

1579 # sqlite has no "FOR UPDATE" AFAICT 

1580 return "" 

1581 

1582 def update_from_clause( 

1583 self, update_stmt, from_table, extra_froms, from_hints, **kw 

1584 ): 

1585 kw["asfrom"] = True 

1586 return "FROM " + ", ".join( 

1587 t._compiler_dispatch(self, fromhints=from_hints, **kw) 

1588 for t in extra_froms 

1589 ) 

1590 

1591 def visit_is_distinct_from_binary(self, binary, operator, **kw): 

1592 return "%s IS NOT %s" % ( 

1593 self.process(binary.left), 

1594 self.process(binary.right), 

1595 ) 

1596 

1597 def visit_is_not_distinct_from_binary(self, binary, operator, **kw): 

1598 return "%s IS %s" % ( 

1599 self.process(binary.left), 

1600 self.process(binary.right), 

1601 ) 

1602 

1603 def visit_json_getitem_op_binary( 

1604 self, binary, operator, _cast_applied=False, **kw 

1605 ): 

1606 if ( 

1607 not _cast_applied 

1608 and binary.type._type_affinity is not sqltypes.JSON 

1609 ): 

1610 kw["_cast_applied"] = True 

1611 return self.process(sql.cast(binary, binary.type), **kw) 

1612 

1613 if binary.type._type_affinity is sqltypes.JSON: 

1614 expr = "JSON_QUOTE(JSON_EXTRACT(%s, %s))" 

1615 else: 

1616 expr = "JSON_EXTRACT(%s, %s)" 

1617 

1618 return expr % ( 

1619 self.process(binary.left, **kw), 

1620 self.process(binary.right, **kw), 

1621 ) 

1622 

1623 def visit_json_path_getitem_op_binary( 

1624 self, binary, operator, _cast_applied=False, **kw 

1625 ): 

1626 if ( 

1627 not _cast_applied 

1628 and binary.type._type_affinity is not sqltypes.JSON 

1629 ): 

1630 kw["_cast_applied"] = True 

1631 return self.process(sql.cast(binary, binary.type), **kw) 

1632 

1633 if binary.type._type_affinity is sqltypes.JSON: 

1634 expr = "JSON_QUOTE(JSON_EXTRACT(%s, %s))" 

1635 else: 

1636 expr = "JSON_EXTRACT(%s, %s)" 

1637 

1638 return expr % ( 

1639 self.process(binary.left, **kw), 

1640 self.process(binary.right, **kw), 

1641 ) 

1642 

1643 def visit_empty_set_op_expr(self, type_, expand_op, **kw): 

1644 # slightly old SQLite versions don't seem to be able to handle 

1645 # the empty set impl 

1646 return self.visit_empty_set_expr(type_) 

1647 

1648 def visit_empty_set_expr(self, element_types, **kw): 

1649 return "SELECT %s FROM (SELECT %s) WHERE 1!=1" % ( 

1650 ", ".join("1" for type_ in element_types or [INTEGER()]), 

1651 ", ".join("1" for type_ in element_types or [INTEGER()]), 

1652 ) 

1653 

1654 def visit_regexp_match_op_binary(self, binary, operator, **kw): 

1655 return self._generate_generic_binary(binary, " REGEXP ", **kw) 

1656 

1657 def visit_not_regexp_match_op_binary(self, binary, operator, **kw): 

1658 return self._generate_generic_binary(binary, " NOT REGEXP ", **kw) 

1659 

1660 def _on_conflict_target(self, clause, **kw): 

1661 if clause.inferred_target_elements is not None: 

1662 target_text = "(%s)" % ", ".join( 

1663 ( 

1664 self.preparer.quote(c) 

1665 if isinstance(c, str) 

1666 else self.process(c, include_table=False, use_schema=False) 

1667 ) 

1668 for c in clause.inferred_target_elements 

1669 ) 

1670 if clause.inferred_target_whereclause is not None: 

1671 whereclause_kw = dict(kw) 

1672 whereclause_kw.update( 

1673 include_table=False, 

1674 use_schema=False, 

1675 literal_execute=True, 

1676 ) 

1677 target_text += " WHERE %s" % self.process( 

1678 clause.inferred_target_whereclause, 

1679 **whereclause_kw, 

1680 ) 

1681 

1682 else: 

1683 target_text = "" 

1684 

1685 return target_text 

1686 

1687 def visit_on_conflict_do_nothing(self, on_conflict, **kw): 

1688 target_text = self._on_conflict_target(on_conflict, **kw) 

1689 

1690 if target_text: 

1691 return "ON CONFLICT %s DO NOTHING" % target_text 

1692 else: 

1693 return "ON CONFLICT DO NOTHING" 

1694 

1695 def visit_on_conflict_do_update(self, on_conflict, **kw): 

1696 clause = on_conflict 

1697 

1698 target_text = self._on_conflict_target(on_conflict, **kw) 

1699 

1700 action_set_ops = [] 

1701 

1702 set_parameters = dict(clause.update_values_to_set) 

1703 # create a list of column assignment clauses as tuples 

1704 

1705 insert_statement = self.stack[-1]["selectable"] 

1706 cols = insert_statement.table.c 

1707 set_kw = dict(kw) 

1708 set_kw.update(use_schema=False) 

1709 for c in cols: 

1710 col_key = c.key 

1711 

1712 if col_key in set_parameters: 

1713 value = set_parameters.pop(col_key) 

1714 elif c in set_parameters: 

1715 value = set_parameters.pop(c) 

1716 else: 

1717 continue 

1718 

1719 if ( 

1720 isinstance(value, elements.BindParameter) 

1721 and value.type._isnull 

1722 ): 

1723 value = value._with_binary_element_type(c.type) 

1724 

1725 value_text = self.process( 

1726 value.self_group(), is_upsert_set=True, **set_kw 

1727 ) 

1728 

1729 key_text = self.preparer.quote(c.name) 

1730 action_set_ops.append("%s = %s" % (key_text, value_text)) 

1731 

1732 # check for names that don't match columns 

1733 if set_parameters: 

1734 util.warn( 

1735 "Additional column names not matching " 

1736 "any column keys in table '%s': %s" 

1737 % ( 

1738 self.current_executable.table.name, 

1739 (", ".join("'%s'" % c for c in set_parameters)), 

1740 ) 

1741 ) 

1742 for k, v in set_parameters.items(): 

1743 key_text = ( 

1744 self.preparer.quote(k) 

1745 if isinstance(k, str) 

1746 else self.process(k, **set_kw) 

1747 ) 

1748 value_text = self.process( 

1749 coercions.expect(roles.ExpressionElementRole, v), 

1750 is_upsert_set=True, 

1751 **set_kw, 

1752 ) 

1753 action_set_ops.append("%s = %s" % (key_text, value_text)) 

1754 

1755 action_text = ", ".join(action_set_ops) 

1756 if clause.update_whereclause is not None: 

1757 where_kw = dict(kw) 

1758 where_kw.update(include_table=True, use_schema=False) 

1759 action_text += " WHERE %s" % self.process( 

1760 clause.update_whereclause, **where_kw 

1761 ) 

1762 

1763 return "ON CONFLICT %s DO UPDATE SET %s" % (target_text, action_text) 

1764 

1765 def visit_bitwise_xor_op_binary(self, binary, operator, **kw): 

1766 # sqlite has no xor. Use "a XOR b" = "(a | b) - (a & b)". 

1767 kw["eager_grouping"] = True 

1768 or_ = self._generate_generic_binary(binary, " | ", **kw) 

1769 and_ = self._generate_generic_binary(binary, " & ", **kw) 

1770 return f"({or_} - {and_})" 

1771 

1772 

1773class SQLiteDDLCompiler(compiler.DDLCompiler): 

1774 def get_column_specification(self, column, **kwargs): 

1775 coltype = self.dialect.type_compiler_instance.process( 

1776 column.type, type_expression=column 

1777 ) 

1778 colspec = self.preparer.format_column(column) + " " + coltype 

1779 default = self.get_column_default_string(column) 

1780 if default is not None: 

1781 

1782 if not re.match(r"""^\s*[\'\"\(]""", default) and re.match( 

1783 r".*\W.*", default 

1784 ): 

1785 colspec += f" DEFAULT ({default})" 

1786 else: 

1787 colspec += f" DEFAULT {default}" 

1788 

1789 if not column.nullable: 

1790 colspec += " NOT NULL" 

1791 

1792 on_conflict_clause = column.dialect_options["sqlite"][ 

1793 "on_conflict_not_null" 

1794 ] 

1795 if on_conflict_clause is not None: 

1796 colspec += " ON CONFLICT " + on_conflict_clause 

1797 

1798 if column.primary_key: 

1799 if ( 

1800 column.autoincrement is True 

1801 and len(column.table.primary_key.columns) != 1 

1802 ): 

1803 raise exc.CompileError( 

1804 "SQLite does not support autoincrement for " 

1805 "composite primary keys" 

1806 ) 

1807 

1808 if ( 

1809 column.table.dialect_options["sqlite"]["autoincrement"] 

1810 and len(column.table.primary_key.columns) == 1 

1811 and issubclass(column.type._type_affinity, sqltypes.Integer) 

1812 and not column.foreign_keys 

1813 ): 

1814 colspec += " PRIMARY KEY" 

1815 

1816 on_conflict_clause = column.dialect_options["sqlite"][ 

1817 "on_conflict_primary_key" 

1818 ] 

1819 if on_conflict_clause is not None: 

1820 colspec += " ON CONFLICT " + on_conflict_clause 

1821 

1822 colspec += " AUTOINCREMENT" 

1823 

1824 if column.computed is not None: 

1825 colspec += " " + self.process(column.computed) 

1826 

1827 return colspec 

1828 

1829 def visit_primary_key_constraint(self, constraint, **kw): 

1830 # for columns with sqlite_autoincrement=True, 

1831 # the PRIMARY KEY constraint can only be inline 

1832 # with the column itself. 

1833 if len(constraint.columns) == 1: 

1834 c = list(constraint)[0] 

1835 if ( 

1836 c.primary_key 

1837 and c.table.dialect_options["sqlite"]["autoincrement"] 

1838 and issubclass(c.type._type_affinity, sqltypes.Integer) 

1839 and not c.foreign_keys 

1840 ): 

1841 return None 

1842 

1843 text = super().visit_primary_key_constraint(constraint) 

1844 

1845 on_conflict_clause = constraint.dialect_options["sqlite"][ 

1846 "on_conflict" 

1847 ] 

1848 if on_conflict_clause is None and len(constraint.columns) == 1: 

1849 on_conflict_clause = list(constraint)[0].dialect_options["sqlite"][ 

1850 "on_conflict_primary_key" 

1851 ] 

1852 

1853 if on_conflict_clause is not None: 

1854 text += " ON CONFLICT " + on_conflict_clause 

1855 

1856 return text 

1857 

1858 def visit_unique_constraint(self, constraint, **kw): 

1859 text = super().visit_unique_constraint(constraint) 

1860 

1861 on_conflict_clause = constraint.dialect_options["sqlite"][ 

1862 "on_conflict" 

1863 ] 

1864 if on_conflict_clause is None and len(constraint.columns) == 1: 

1865 col1 = list(constraint)[0] 

1866 if isinstance(col1, schema.SchemaItem): 

1867 on_conflict_clause = list(constraint)[0].dialect_options[ 

1868 "sqlite" 

1869 ]["on_conflict_unique"] 

1870 

1871 if on_conflict_clause is not None: 

1872 text += " ON CONFLICT " + on_conflict_clause 

1873 

1874 return text 

1875 

1876 def visit_check_constraint(self, constraint, **kw): 

1877 text = super().visit_check_constraint(constraint) 

1878 

1879 on_conflict_clause = constraint.dialect_options["sqlite"][ 

1880 "on_conflict" 

1881 ] 

1882 

1883 if on_conflict_clause is not None: 

1884 text += " ON CONFLICT " + on_conflict_clause 

1885 

1886 return text 

1887 

1888 def visit_column_check_constraint(self, constraint, **kw): 

1889 text = super().visit_column_check_constraint(constraint) 

1890 

1891 if constraint.dialect_options["sqlite"]["on_conflict"] is not None: 

1892 raise exc.CompileError( 

1893 "SQLite does not support on conflict clause for " 

1894 "column check constraint" 

1895 ) 

1896 

1897 return text 

1898 

1899 def visit_foreign_key_constraint(self, constraint, **kw): 

1900 local_table = constraint.elements[0].parent.table 

1901 remote_table = constraint.elements[0].column.table 

1902 

1903 if local_table.schema != remote_table.schema: 

1904 return None 

1905 else: 

1906 return super().visit_foreign_key_constraint(constraint) 

1907 

1908 def define_constraint_remote_table(self, constraint, table, preparer): 

1909 """Format the remote table clause of a CREATE CONSTRAINT clause.""" 

1910 

1911 return preparer.format_table(table, use_schema=False) 

1912 

1913 def visit_create_index( 

1914 self, create, include_schema=False, include_table_schema=True, **kw 

1915 ): 

1916 index = create.element 

1917 self._verify_index_table(index) 

1918 preparer = self.preparer 

1919 text = "CREATE " 

1920 if index.unique: 

1921 text += "UNIQUE " 

1922 

1923 text += "INDEX " 

1924 

1925 if create.if_not_exists: 

1926 text += "IF NOT EXISTS " 

1927 

1928 text += "%s ON %s (%s)" % ( 

1929 self._prepared_index_name(index, include_schema=True), 

1930 preparer.format_table(index.table, use_schema=False), 

1931 ", ".join( 

1932 self.sql_compiler.process( 

1933 expr, include_table=False, literal_binds=True 

1934 ) 

1935 for expr in index.expressions 

1936 ), 

1937 ) 

1938 

1939 whereclause = index.dialect_options["sqlite"]["where"] 

1940 if whereclause is not None: 

1941 where_compiled = self.sql_compiler.process( 

1942 whereclause, include_table=False, literal_binds=True 

1943 ) 

1944 text += " WHERE " + where_compiled 

1945 

1946 return text 

1947 

1948 def post_create_table(self, table): 

1949 table_options = [] 

1950 

1951 if not table.dialect_options["sqlite"]["with_rowid"]: 

1952 table_options.append("WITHOUT ROWID") 

1953 

1954 if table.dialect_options["sqlite"]["strict"]: 

1955 table_options.append("STRICT") 

1956 

1957 if table_options: 

1958 return "\n " + ",\n ".join(table_options) 

1959 else: 

1960 return "" 

1961 

1962 def visit_create_view(self, create, **kw): 

1963 """Handle SQLite if_not_exists dialect option for CREATE VIEW.""" 

1964 # Get the if_not_exists dialect option from the CreateView object 

1965 if_not_exists = create.dialect_options["sqlite"].get( 

1966 "if_not_exists", False 

1967 ) 

1968 

1969 # Pass if_not_exists through kw to the parent's _generate_table_select 

1970 kw["if_not_exists"] = if_not_exists 

1971 return super().visit_create_view(create, **kw) 

1972 

1973 

1974class SQLiteTypeCompiler(compiler.GenericTypeCompiler): 

1975 def visit_large_binary(self, type_, **kw): 

1976 return self.visit_BLOB(type_) 

1977 

1978 def visit_DATETIME(self, type_, **kw): 

1979 if ( 

1980 not isinstance(type_, _DateTimeMixin) 

1981 or type_.format_is_text_affinity 

1982 ): 

1983 return super().visit_DATETIME(type_) 

1984 else: 

1985 return "DATETIME_CHAR" 

1986 

1987 def visit_DATE(self, type_, **kw): 

1988 if ( 

1989 not isinstance(type_, _DateTimeMixin) 

1990 or type_.format_is_text_affinity 

1991 ): 

1992 return super().visit_DATE(type_) 

1993 else: 

1994 return "DATE_CHAR" 

1995 

1996 def visit_TIME(self, type_, **kw): 

1997 if ( 

1998 not isinstance(type_, _DateTimeMixin) 

1999 or type_.format_is_text_affinity 

2000 ): 

2001 return super().visit_TIME(type_) 

2002 else: 

2003 return "TIME_CHAR" 

2004 

2005 def visit_JSON(self, type_, **kw): 

2006 # note this name provides NUMERIC affinity, not TEXT. 

2007 # should not be an issue unless the JSON value consists of a single 

2008 # numeric value. JSONTEXT can be used if this case is required. 

2009 return "JSON" 

2010 

2011 def visit_JSONB(self, type_, **kw): 

2012 return "JSONB" 

2013 

2014 

2015class SQLiteIdentifierPreparer(compiler.IdentifierPreparer): 

2016 reserved_words = { 

2017 "add", 

2018 "after", 

2019 "all", 

2020 "alter", 

2021 "analyze", 

2022 "and", 

2023 "as", 

2024 "asc", 

2025 "attach", 

2026 "autoincrement", 

2027 "before", 

2028 "begin", 

2029 "between", 

2030 "by", 

2031 "cascade", 

2032 "case", 

2033 "cast", 

2034 "check", 

2035 "collate", 

2036 "column", 

2037 "commit", 

2038 "conflict", 

2039 "constraint", 

2040 "create", 

2041 "cross", 

2042 "current_date", 

2043 "current_time", 

2044 "current_timestamp", 

2045 "database", 

2046 "default", 

2047 "deferrable", 

2048 "deferred", 

2049 "delete", 

2050 "desc", 

2051 "detach", 

2052 "distinct", 

2053 "drop", 

2054 "each", 

2055 "else", 

2056 "end", 

2057 "escape", 

2058 "except", 

2059 "exclusive", 

2060 "exists", 

2061 "explain", 

2062 "false", 

2063 "fail", 

2064 "for", 

2065 "foreign", 

2066 "from", 

2067 "full", 

2068 "glob", 

2069 "group", 

2070 "having", 

2071 "if", 

2072 "ignore", 

2073 "immediate", 

2074 "in", 

2075 "index", 

2076 "indexed", 

2077 "initially", 

2078 "inner", 

2079 "insert", 

2080 "instead", 

2081 "intersect", 

2082 "into", 

2083 "is", 

2084 "isnull", 

2085 "join", 

2086 "key", 

2087 "left", 

2088 "like", 

2089 "limit", 

2090 "match", 

2091 "natural", 

2092 "not", 

2093 "notnull", 

2094 "null", 

2095 "of", 

2096 "offset", 

2097 "on", 

2098 "or", 

2099 "order", 

2100 "outer", 

2101 "plan", 

2102 "pragma", 

2103 "primary", 

2104 "query", 

2105 "raise", 

2106 "references", 

2107 "reindex", 

2108 "rename", 

2109 "replace", 

2110 "restrict", 

2111 "right", 

2112 "rollback", 

2113 "row", 

2114 "select", 

2115 "set", 

2116 "table", 

2117 "temp", 

2118 "temporary", 

2119 "then", 

2120 "to", 

2121 "transaction", 

2122 "trigger", 

2123 "true", 

2124 "union", 

2125 "unique", 

2126 "update", 

2127 "using", 

2128 "vacuum", 

2129 "values", 

2130 "view", 

2131 "virtual", 

2132 "when", 

2133 "where", 

2134 } 

2135 

2136 

2137class SQLiteExecutionContext(default.DefaultExecutionContext): 

2138 @util.memoized_property 

2139 def _preserve_raw_colnames(self): 

2140 return ( 

2141 not self.dialect._broken_dotted_colnames 

2142 or self.execution_options.get("sqlite_raw_colnames", False) 

2143 ) 

2144 

2145 def _translate_colname(self, colname): 

2146 # TODO: detect SQLite version 3.10.0 or greater; 

2147 # see [ticket:3633] 

2148 

2149 # adjust for dotted column names. SQLite 

2150 # in the case of UNION may store col names as 

2151 # "tablename.colname", or if using an attached database, 

2152 # "database.tablename.colname", in cursor.description 

2153 if not self._preserve_raw_colnames and "." in colname: 

2154 return colname.split(".")[-1], colname 

2155 else: 

2156 return colname, None 

2157 

2158 

2159# regexp that locates a FOREIGN KEY clause within the verbatim CREATE TABLE 

2160# text that sqlite stores in sqlite_master. the referred-columns group 

2161# requires a non-empty separator between column tokens; an earlier form made 

2162# the separator optional, which turned the repeat into a nested quantifier and 

2163# let a long word run backtrack exponentially. kept at module level so it can 

2164# be exercised directly from the tests. 

2165FK_PATTERN = re.compile( 

2166 r'(?:CONSTRAINT\s+(?:"(.+?)"|(\w+))\s+)?' 

2167 r"FOREIGN\s+KEY\s*\(\s*(.+?)\s*\)\s+" 

2168 r'REFERENCES\s+(?:(?:"(.+?)")|([a-z0-9_]+))\s*\(\s*((?:"[^"]+"|[a-z0-9_]+)(?:(?:\s*,\s*|\s+)(?:"[^"]+"|[a-z0-9_]+))*\s*)\)\s*' # noqa: E501 

2169 r"((?:ON\s+(?:DELETE|UPDATE)\s+" 

2170 r"(?:SET\s+NULL|SET\s+DEFAULT|CASCADE|RESTRICT|" 

2171 r"NO\s+ACTION)\s*)*)" 

2172 r"((?:NOT\s+)?DEFERRABLE)?" 

2173 r"(?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))?", 

2174 re.I, 

2175) 

2176 

2177# regexp that locates the table option keywords which may trail the closing 

2178# paren of the column list in the verbatim CREATE TABLE text. the match is 

2179# anchored at the end of the statement, which makes the closing paren 

2180# unambiguous, as neither keyword can itself contain one. the keywords may 

2181# be given in either order; as each may appear only once, the repeated group 

2182# leaves one named group per keyword holding the text that was matched. 

2183TABLE_OPTIONS_PATTERN = re.compile( 

2184 r"\)(?:\s*,?\s*(?:(?P<without_rowid>WITHOUT\s+ROWID)" 

2185 r"|(?P<strict>STRICT)))*\s*$", 

2186 re.I, 

2187) 

2188 

2189 

2190class SQLiteDialect(default.DefaultDialect): 

2191 name = "sqlite" 

2192 supports_alter = False 

2193 

2194 # SQlite supports "DEFAULT VALUES" but *does not* support 

2195 # "VALUES (DEFAULT)" 

2196 supports_default_values = True 

2197 supports_default_metavalue = False 

2198 

2199 # sqlite issue: 

2200 # https://github.com/python/cpython/issues/93421 

2201 # note this parameter is no longer used by the ORM or default dialect 

2202 # see #9414 

2203 supports_sane_rowcount_returning = False 

2204 

2205 supports_empty_insert = False 

2206 supports_cast = True 

2207 supports_multivalues_insert = True 

2208 use_insertmanyvalues = True 

2209 tuple_in_values = True 

2210 supports_statement_cache = True 

2211 insert_null_pk_still_autoincrements = True 

2212 insert_returning = True 

2213 update_returning = True 

2214 update_returning_multifrom = True 

2215 delete_returning = True 

2216 update_returning_multifrom = True 

2217 

2218 supports_default_metavalue = True 

2219 """dialect supports INSERT... VALUES (DEFAULT) syntax""" 

2220 

2221 default_metavalue_token = "NULL" 

2222 """for INSERT... VALUES (DEFAULT) syntax, the token to put in the 

2223 parenthesis.""" 

2224 

2225 default_paramstyle = "qmark" 

2226 execution_ctx_cls = SQLiteExecutionContext 

2227 statement_compiler = SQLiteCompiler 

2228 ddl_compiler = SQLiteDDLCompiler 

2229 type_compiler_cls = SQLiteTypeCompiler 

2230 preparer = SQLiteIdentifierPreparer 

2231 ischema_names = ischema_names 

2232 colspecs = colspecs 

2233 

2234 construct_arguments = [ 

2235 ( 

2236 sa_schema.Table, 

2237 { 

2238 "autoincrement": False, 

2239 "with_rowid": True, 

2240 "strict": False, 

2241 }, 

2242 ), 

2243 (sa_schema.Index, {"where": None}), 

2244 ( 

2245 sa_schema.Column, 

2246 { 

2247 "on_conflict_primary_key": None, 

2248 "on_conflict_not_null": None, 

2249 "on_conflict_unique": None, 

2250 }, 

2251 ), 

2252 (sa_schema.Constraint, {"on_conflict": None}), 

2253 (sa_ddl.CreateView, {"if_not_exists": False}), 

2254 ] 

2255 

2256 _broken_fk_pragma_quotes = False 

2257 _broken_dotted_colnames = False 

2258 

2259 def __init__( 

2260 self, 

2261 native_datetime: bool = False, 

2262 json_serializer: Callable[[_JSON_VALUE], str] | None = None, 

2263 json_deserializer: Callable[[str], _JSON_VALUE] | None = None, 

2264 **kwargs: Any, 

2265 ) -> None: 

2266 default.DefaultDialect.__init__(self, **kwargs) 

2267 

2268 self._json_serializer = json_serializer 

2269 self._json_deserializer = json_deserializer 

2270 

2271 # this flag used by pysqlite dialect, and perhaps others in the 

2272 # future, to indicate the driver is handling date/timestamp 

2273 # conversions (and perhaps datetime/time as well on some hypothetical 

2274 # driver ?) 

2275 self.native_datetime = native_datetime 

2276 

2277 if self.dbapi is not None: 

2278 if self.dbapi.sqlite_version_info < (3, 7, 16): 

2279 util.warn( 

2280 "SQLite version %s is older than 3.7.16, and will not " 

2281 "support right nested joins, as are sometimes used in " 

2282 "more complex ORM scenarios. SQLAlchemy 1.4 and above " 

2283 "no longer tries to rewrite these joins." 

2284 % (self.dbapi.sqlite_version_info,) 

2285 ) 

2286 

2287 # NOTE: python 3.7 on fedora for me has SQLite 3.34.1. These 

2288 # version checks are getting very stale. 

2289 self._broken_dotted_colnames = self.dbapi.sqlite_version_info < ( 

2290 3, 

2291 10, 

2292 0, 

2293 ) 

2294 self.supports_default_values = self.dbapi.sqlite_version_info >= ( 

2295 3, 

2296 3, 

2297 8, 

2298 ) 

2299 self.supports_cast = self.dbapi.sqlite_version_info >= (3, 2, 3) 

2300 self.supports_multivalues_insert = ( 

2301 # https://www.sqlite.org/releaselog/3_7_11.html 

2302 self.dbapi.sqlite_version_info 

2303 >= (3, 7, 11) 

2304 ) 

2305 # see https://www.sqlalchemy.org/trac/ticket/2568 

2306 # as well as https://www.sqlite.org/src/info/600482d161 

2307 self._broken_fk_pragma_quotes = self.dbapi.sqlite_version_info < ( 

2308 3, 

2309 6, 

2310 14, 

2311 ) 

2312 

2313 if self.dbapi.sqlite_version_info < (3, 35): 

2314 self.update_returning = self.delete_returning = ( 

2315 self.insert_returning 

2316 ) = False 

2317 

2318 if self.dbapi.sqlite_version_info < (3, 32, 0): 

2319 # https://www.sqlite.org/limits.html 

2320 self.insertmanyvalues_max_parameters = 999 

2321 

2322 _isolation_lookup = util.immutabledict( 

2323 {"READ UNCOMMITTED": 1, "SERIALIZABLE": 0} 

2324 ) 

2325 

2326 def get_isolation_level_values(self, dbapi_connection): 

2327 return list(self._isolation_lookup) 

2328 

2329 def set_isolation_level( 

2330 self, dbapi_connection: DBAPIConnection, level: IsolationLevel 

2331 ) -> None: 

2332 isolation_level = self._isolation_lookup[level] 

2333 

2334 cursor = dbapi_connection.cursor() 

2335 cursor.execute(f"PRAGMA read_uncommitted = {isolation_level}") 

2336 cursor.close() 

2337 

2338 def get_isolation_level(self, dbapi_connection): 

2339 cursor = dbapi_connection.cursor() 

2340 cursor.execute("PRAGMA read_uncommitted") 

2341 res = cursor.fetchone() 

2342 if res: 

2343 value = res[0] 

2344 else: 

2345 # https://www.sqlite.org/changes.html#version_3_3_3 

2346 # "Optional READ UNCOMMITTED isolation (instead of the 

2347 # default isolation level of SERIALIZABLE) and 

2348 # table level locking when database connections 

2349 # share a common cache."" 

2350 # pre-SQLite 3.3.0 default to 0 

2351 value = 0 

2352 cursor.close() 

2353 if value == 0: 

2354 return "SERIALIZABLE" 

2355 elif value == 1: 

2356 return "READ UNCOMMITTED" 

2357 else: 

2358 assert False, "Unknown isolation level %s" % value 

2359 

2360 @reflection.cache 

2361 def get_schema_names(self, connection, **kw): 

2362 s = "PRAGMA database_list" 

2363 dl = connection.exec_driver_sql(s) 

2364 

2365 return [db[1] for db in dl if db[1] != "temp"] 

2366 

2367 def _format_schema(self, schema, table_name): 

2368 if schema is not None: 

2369 qschema = self.identifier_preparer.quote_identifier(schema) 

2370 name = f"{qschema}.{table_name}" 

2371 else: 

2372 name = table_name 

2373 return name 

2374 

2375 def _sqlite_main_query( 

2376 self, 

2377 table: str, 

2378 type_: str, 

2379 schema: Optional[str], 

2380 sqlite_include_internal: bool, 

2381 ): 

2382 main = self._format_schema(schema, table) 

2383 if not sqlite_include_internal: 

2384 filter_table = " AND name NOT LIKE 'sqlite~_%' ESCAPE '~'" 

2385 else: 

2386 filter_table = "" 

2387 query = ( 

2388 f"SELECT name FROM {main} " 

2389 f"WHERE type='{type_}'{filter_table} " 

2390 "ORDER BY name" 

2391 ) 

2392 return query 

2393 

2394 @reflection.cache 

2395 def get_table_names( 

2396 self, connection, schema=None, sqlite_include_internal=False, **kw 

2397 ): 

2398 query = self._sqlite_main_query( 

2399 "sqlite_master", "table", schema, sqlite_include_internal 

2400 ) 

2401 names = connection.exec_driver_sql(query).scalars().all() 

2402 return names 

2403 

2404 @reflection.cache 

2405 def get_temp_table_names( 

2406 self, connection, sqlite_include_internal=False, **kw 

2407 ): 

2408 query = self._sqlite_main_query( 

2409 "sqlite_temp_master", "table", None, sqlite_include_internal 

2410 ) 

2411 names = connection.exec_driver_sql(query).scalars().all() 

2412 return names 

2413 

2414 @reflection.cache 

2415 def get_temp_view_names( 

2416 self, connection, sqlite_include_internal=False, **kw 

2417 ): 

2418 query = self._sqlite_main_query( 

2419 "sqlite_temp_master", "view", None, sqlite_include_internal 

2420 ) 

2421 names = connection.exec_driver_sql(query).scalars().all() 

2422 return names 

2423 

2424 @reflection.cache 

2425 def has_table(self, connection, table_name, schema=None, **kw): 

2426 self._ensure_has_table_connection(connection) 

2427 

2428 if schema is not None and schema not in self.get_schema_names( 

2429 connection, **kw 

2430 ): 

2431 return False 

2432 

2433 info = self._get_table_pragma( 

2434 connection, "table_info", table_name, schema=schema 

2435 ) 

2436 return bool(info) 

2437 

2438 def _get_default_schema_name(self, connection): 

2439 return "main" 

2440 

2441 @reflection.cache 

2442 def get_view_names( 

2443 self, connection, schema=None, sqlite_include_internal=False, **kw 

2444 ): 

2445 query = self._sqlite_main_query( 

2446 "sqlite_master", "view", schema, sqlite_include_internal 

2447 ) 

2448 names = connection.exec_driver_sql(query).scalars().all() 

2449 return names 

2450 

2451 @reflection.cache 

2452 def get_view_definition(self, connection, view_name, schema=None, **kw): 

2453 if schema is not None: 

2454 qschema = self.identifier_preparer.quote_identifier(schema) 

2455 master = f"{qschema}.sqlite_master" 

2456 s = ("SELECT sql FROM %s WHERE name = ? AND type='view'") % ( 

2457 master, 

2458 ) 

2459 rs = connection.exec_driver_sql(s, (view_name,)) 

2460 else: 

2461 try: 

2462 s = ( 

2463 "SELECT sql FROM " 

2464 " (SELECT * FROM sqlite_master UNION ALL " 

2465 " SELECT * FROM sqlite_temp_master) " 

2466 "WHERE name = ? " 

2467 "AND type='view'" 

2468 ) 

2469 rs = connection.exec_driver_sql(s, (view_name,)) 

2470 except exc.DBAPIError: 

2471 s = ( 

2472 "SELECT sql FROM sqlite_master WHERE name = ? " 

2473 "AND type='view'" 

2474 ) 

2475 rs = connection.exec_driver_sql(s, (view_name,)) 

2476 

2477 result = rs.fetchall() 

2478 if result: 

2479 return result[0].sql 

2480 else: 

2481 raise exc.NoSuchTableError( 

2482 f"{schema}.{view_name}" if schema else view_name 

2483 ) 

2484 

2485 @reflection.cache 

2486 def get_table_options(self, connection, table_name, schema=None, **kw): 

2487 tablesql = self._get_table_sql( 

2488 connection, table_name, schema=schema, **kw 

2489 ) 

2490 

2491 options = {} 

2492 

2493 # tablesql is None for the internal sqlite_ tables, which have no 

2494 # entry in sqlite_master 

2495 if tablesql is not None: 

2496 match = TABLE_OPTIONS_PATTERN.search(tablesql.strip()) 

2497 if match: 

2498 if match.group("without_rowid"): 

2499 options["sqlite_with_rowid"] = False 

2500 if match.group("strict"): 

2501 options["sqlite_strict"] = True 

2502 

2503 if options: 

2504 return options 

2505 else: 

2506 return ReflectionDefaults.table_options() 

2507 

2508 @reflection.cache 

2509 def get_columns(self, connection, table_name, schema=None, **kw): 

2510 pragma = "table_info" 

2511 # computed columns are threaded as hidden, they require table_xinfo 

2512 if self.server_version_info >= (3, 31): 

2513 pragma = "table_xinfo" 

2514 info = self._get_table_pragma( 

2515 connection, pragma, table_name, schema=schema 

2516 ) 

2517 columns = [] 

2518 tablesql = None 

2519 for row in info: 

2520 name = row[1] 

2521 type_ = row[2].upper() 

2522 nullable = not row[3] 

2523 default = row[4] 

2524 primary_key = row[5] 

2525 hidden = row[6] if pragma == "table_xinfo" else 0 

2526 

2527 # hidden has value 0 for normal columns, 1 for hidden columns, 

2528 # 2 for computed virtual columns and 3 for computed stored columns 

2529 # https://www.sqlite.org/src/info/069351b85f9a706f60d3e98fbc8aaf40c374356b967c0464aede30ead3d9d18b 

2530 if hidden == 1: 

2531 continue 

2532 

2533 generated = bool(hidden) 

2534 persisted = hidden == 3 

2535 

2536 if tablesql is None and generated: 

2537 tablesql = self._get_table_sql( 

2538 connection, table_name, schema, **kw 

2539 ) 

2540 # remove create table 

2541 match = re.match( 

2542 ( 

2543 r"create table .*?\((.*)\)" 

2544 r"(?:\s*,?\s*(?:WITHOUT\s+ROWID|STRICT))*$" 

2545 ), 

2546 tablesql.strip(), 

2547 re.DOTALL | re.IGNORECASE, 

2548 ) 

2549 assert match, f"create table not found in {tablesql}" 

2550 tablesql = match.group(1).strip() 

2551 

2552 columns.append( 

2553 self._get_column_info( 

2554 name, 

2555 type_, 

2556 nullable, 

2557 default, 

2558 primary_key, 

2559 generated, 

2560 persisted, 

2561 tablesql, 

2562 ) 

2563 ) 

2564 if columns: 

2565 return columns 

2566 elif not self.has_table(connection, table_name, schema): 

2567 raise exc.NoSuchTableError( 

2568 f"{schema}.{table_name}" if schema else table_name 

2569 ) 

2570 else: 

2571 return ReflectionDefaults.columns() 

2572 

2573 def _get_column_info( 

2574 self, 

2575 name, 

2576 type_, 

2577 nullable, 

2578 default, 

2579 primary_key, 

2580 generated, 

2581 persisted, 

2582 tablesql, 

2583 ): 

2584 if generated: 

2585 # the type of a column "cc INTEGER GENERATED ALWAYS AS (1 + 42)" 

2586 # somehow is "INTEGER GENERATED ALWAYS" 

2587 type_ = re.sub("generated", "", type_, flags=re.IGNORECASE) 

2588 type_ = re.sub("always", "", type_, flags=re.IGNORECASE).strip() 

2589 

2590 coltype = self._resolve_type_affinity(type_) 

2591 

2592 if default is not None: 

2593 default = str(default) 

2594 

2595 colspec = { 

2596 "name": name, 

2597 "type": coltype, 

2598 "nullable": nullable, 

2599 "default": default, 

2600 "primary_key": primary_key, 

2601 } 

2602 if generated: 

2603 sqltext = "" 

2604 if tablesql: 

2605 pattern = ( 

2606 r"[^,]*\s+GENERATED\s+ALWAYS\s+AS" 

2607 r"\s+\((.*)\)\s*(?:virtual|stored)?" 

2608 ) 

2609 match = re.search( 

2610 re.escape(name) + pattern, tablesql, re.IGNORECASE 

2611 ) 

2612 if match: 

2613 sqltext = match.group(1) 

2614 colspec["computed"] = {"sqltext": sqltext, "persisted": persisted} 

2615 return colspec 

2616 

2617 def _resolve_type_affinity(self, type_): 

2618 """Return a data type from a reflected column, using affinity rules. 

2619 

2620 SQLite's goal for universal compatibility introduces some complexity 

2621 during reflection, as a column's defined type might not actually be a 

2622 type that SQLite understands - or indeed, my not be defined *at all*. 

2623 Internally, SQLite handles this with a 'data type affinity' for each 

2624 column definition, mapping to one of 'TEXT', 'NUMERIC', 'INTEGER', 

2625 'REAL', or 'NONE' (raw bits). The algorithm that determines this is 

2626 listed in https://www.sqlite.org/datatype3.html section 2.1. 

2627 

2628 This method allows SQLAlchemy to support that algorithm, while still 

2629 providing access to smarter reflection utilities by recognizing 

2630 column definitions that SQLite only supports through affinity (like 

2631 DATE and DOUBLE). 

2632 

2633 """ 

2634 match = re.match(r"([\w ]+)(\(.*?\))?", type_) 

2635 if match: 

2636 coltype = match.group(1) 

2637 args = match.group(2) 

2638 else: 

2639 coltype = "" 

2640 args = "" 

2641 

2642 if coltype in self.ischema_names: 

2643 coltype = self.ischema_names[coltype] 

2644 elif "INT" in coltype: 

2645 coltype = sqltypes.INTEGER 

2646 elif "CHAR" in coltype or "CLOB" in coltype or "TEXT" in coltype: 

2647 coltype = sqltypes.TEXT 

2648 elif "BLOB" in coltype or not coltype: 

2649 coltype = sqltypes.NullType 

2650 elif "REAL" in coltype or "FLOA" in coltype or "DOUB" in coltype: 

2651 coltype = sqltypes.REAL 

2652 else: 

2653 coltype = sqltypes.NUMERIC 

2654 

2655 if args is not None: 

2656 args = re.findall(r"(\d+)", args) 

2657 try: 

2658 coltype = coltype(*[int(a) for a in args]) 

2659 except TypeError: 

2660 util.warn( 

2661 "Could not instantiate type %s with " 

2662 "reflected arguments %s; using no arguments." 

2663 % (coltype, args) 

2664 ) 

2665 coltype = coltype() 

2666 else: 

2667 coltype = coltype() 

2668 

2669 return coltype 

2670 

2671 @reflection.cache 

2672 def get_pk_constraint(self, connection, table_name, schema=None, **kw): 

2673 constraint_name = None 

2674 table_data = self._get_table_sql(connection, table_name, schema=schema) 

2675 if table_data: 

2676 PK_PATTERN = r'CONSTRAINT\s+(?:"(.+?)"|(\w+))\s+PRIMARY\s+KEY' 

2677 result = re.search(PK_PATTERN, table_data, re.I) 

2678 if result: 

2679 constraint_name = result.group(1) or result.group(2) 

2680 else: 

2681 constraint_name = None 

2682 

2683 cols = self.get_columns(connection, table_name, schema, **kw) 

2684 # consider only pk columns. This also avoids sorting the cached 

2685 # value returned by get_columns 

2686 cols = [col for col in cols if col.get("primary_key", 0) > 0] 

2687 cols.sort(key=lambda col: col.get("primary_key")) 

2688 pkeys = [col["name"] for col in cols] 

2689 

2690 if pkeys: 

2691 return {"constrained_columns": pkeys, "name": constraint_name} 

2692 else: 

2693 return ReflectionDefaults.pk_constraint() 

2694 

2695 @reflection.cache 

2696 def get_foreign_keys(self, connection, table_name, schema=None, **kw): 

2697 # sqlite makes this *extremely difficult*. 

2698 # First, use the pragma to get the actual FKs. 

2699 pragma_fks = self._get_table_pragma( 

2700 connection, "foreign_key_list", table_name, schema=schema 

2701 ) 

2702 

2703 fks = {} 

2704 

2705 for row in pragma_fks: 

2706 numerical_id, rtbl, lcol, rcol = (row[0], row[2], row[3], row[4]) 

2707 

2708 if not rcol: 

2709 # no referred column, which means it was not named in the 

2710 # original DDL. The referred columns of the foreign key 

2711 # constraint are therefore the primary key of the referred 

2712 # table. 

2713 try: 

2714 referred_pk = self.get_pk_constraint( 

2715 connection, rtbl, schema=schema, **kw 

2716 ) 

2717 referred_columns = referred_pk["constrained_columns"] 

2718 except exc.NoSuchTableError: 

2719 # ignore not existing parents 

2720 referred_columns = [] 

2721 else: 

2722 # note we use this list only if this is the first column 

2723 # in the constraint. for subsequent columns we ignore the 

2724 # list and append "rcol" if present. 

2725 referred_columns = [] 

2726 

2727 if self._broken_fk_pragma_quotes: 

2728 rtbl = re.sub(r"^[\"\[`\']|[\"\]`\']$", "", rtbl) 

2729 

2730 if numerical_id in fks: 

2731 fk = fks[numerical_id] 

2732 else: 

2733 fk = fks[numerical_id] = { 

2734 "name": None, 

2735 "constrained_columns": [], 

2736 "referred_schema": schema, 

2737 "referred_table": rtbl, 

2738 "referred_columns": referred_columns, 

2739 "options": {}, 

2740 } 

2741 fks[numerical_id] = fk 

2742 

2743 fk["constrained_columns"].append(lcol) 

2744 

2745 if rcol: 

2746 fk["referred_columns"].append(rcol) 

2747 

2748 def fk_sig(constrained_columns, referred_table, referred_columns): 

2749 return ( 

2750 tuple(constrained_columns) 

2751 + (referred_table,) 

2752 + tuple(referred_columns) 

2753 ) 

2754 

2755 # then, parse the actual SQL and attempt to find DDL that matches 

2756 # the names as well. SQLite saves the DDL in whatever format 

2757 # it was typed in as, so need to be liberal here. 

2758 

2759 keys_by_signature = { 

2760 fk_sig( 

2761 fk["constrained_columns"], 

2762 fk["referred_table"], 

2763 fk["referred_columns"], 

2764 ): fk 

2765 for fk in fks.values() 

2766 } 

2767 

2768 table_data = self._get_table_sql(connection, table_name, schema=schema) 

2769 

2770 def parse_fks(): 

2771 if table_data is None: 

2772 # system tables, etc. 

2773 return 

2774 

2775 # note that we already have the FKs from PRAGMA above. This whole 

2776 # regexp thing is trying to locate additional detail about the 

2777 # FKs, namely the name of the constraint and other options. 

2778 # so parsing the columns is really about matching it up to what 

2779 # we already have. 

2780 for match in FK_PATTERN.finditer(table_data): 

2781 ( 

2782 constraint_quoted_name, 

2783 constraint_name, 

2784 constrained_columns, 

2785 referred_quoted_name, 

2786 referred_name, 

2787 referred_columns, 

2788 onupdatedelete, 

2789 deferrable, 

2790 initially, 

2791 ) = match.group(1, 2, 3, 4, 5, 6, 7, 8, 9) 

2792 constraint_name = constraint_quoted_name or constraint_name 

2793 constrained_columns = list( 

2794 self._find_cols_in_sig(constrained_columns) 

2795 ) 

2796 if not referred_columns: 

2797 referred_columns = constrained_columns 

2798 else: 

2799 referred_columns = list( 

2800 self._find_cols_in_sig(referred_columns) 

2801 ) 

2802 referred_name = referred_quoted_name or referred_name 

2803 options = {} 

2804 

2805 # a newline may separate the words of an 

2806 # ON DELETE / ON UPDATE clause; normalize to single 

2807 # spaces so the tokens below compare correctly 

2808 onupdatedelete = re.sub( 

2809 r"\s+", " ", onupdatedelete.upper() 

2810 ).strip() 

2811 for token in re.split(r" *\bON\b *", onupdatedelete): 

2812 if token.startswith("DELETE"): 

2813 ondelete = token[6:].strip() 

2814 if ondelete and ondelete != "NO ACTION": 

2815 options["ondelete"] = ondelete 

2816 elif token.startswith("UPDATE"): 

2817 onupdate = token[6:].strip() 

2818 if onupdate and onupdate != "NO ACTION": 

2819 options["onupdate"] = onupdate 

2820 

2821 if deferrable: 

2822 options["deferrable"] = "NOT" not in deferrable.upper() 

2823 if initially: 

2824 options["initially"] = initially.upper() 

2825 

2826 yield ( 

2827 constraint_name, 

2828 constrained_columns, 

2829 referred_name, 

2830 referred_columns, 

2831 options, 

2832 ) 

2833 

2834 fkeys = [] 

2835 

2836 for ( 

2837 constraint_name, 

2838 constrained_columns, 

2839 referred_name, 

2840 referred_columns, 

2841 options, 

2842 ) in parse_fks(): 

2843 sig = fk_sig(constrained_columns, referred_name, referred_columns) 

2844 if sig not in keys_by_signature: 

2845 util.warn( 

2846 "WARNING: SQL-parsed foreign key constraint " 

2847 "'%s' could not be located in PRAGMA " 

2848 "foreign_keys for table %s" % (sig, table_name) 

2849 ) 

2850 continue 

2851 key = keys_by_signature.pop(sig) 

2852 key["name"] = constraint_name 

2853 key["options"] = options 

2854 fkeys.append(key) 

2855 # assume the remainders are the unnamed, inline constraints, just 

2856 # use them as is as it's extremely difficult to parse inline 

2857 # constraints 

2858 fkeys.extend(keys_by_signature.values()) 

2859 if fkeys: 

2860 return fkeys 

2861 else: 

2862 return ReflectionDefaults.foreign_keys() 

2863 

2864 def _find_cols_in_sig(self, sig): 

2865 for match in re.finditer(r'(?:"(.+?)")|([a-z0-9_]+)', sig, re.I): 

2866 yield match.group(1) or match.group(2) 

2867 

2868 @reflection.cache 

2869 def get_unique_constraints( 

2870 self, connection, table_name, schema=None, **kw 

2871 ): 

2872 auto_index_by_sig = {} 

2873 for idx in self.get_indexes( 

2874 connection, 

2875 table_name, 

2876 schema=schema, 

2877 include_auto_indexes=True, 

2878 **kw, 

2879 ): 

2880 if not idx["name"].startswith("sqlite_autoindex"): 

2881 continue 

2882 sig = tuple(idx["column_names"]) 

2883 auto_index_by_sig[sig] = idx 

2884 

2885 table_data = self._get_table_sql( 

2886 connection, table_name, schema=schema, **kw 

2887 ) 

2888 unique_constraints = [] 

2889 

2890 def parse_uqs(): 

2891 if table_data is None: 

2892 return 

2893 UNIQUE_PATTERN = ( 

2894 r'(?:CONSTRAINT\s+(?:"(.+?)"|(\w+))\s+)?UNIQUE\s*\((.+?)\)' 

2895 ) 

2896 INLINE_UNIQUE_PATTERN = ( 

2897 r'(?:(".+?")|(?:[\[`])?([a-z0-9_]+)(?:[\]`])?)[\t ]' 

2898 r"+[a-z0-9_]+(?:[\t ]+[a-z0-9_]+)*?[\t ]+UNIQUE" 

2899 ) 

2900 

2901 for match in re.finditer(UNIQUE_PATTERN, table_data, re.I): 

2902 quoted_name, unquoted_name, cols = match.group(1, 2, 3) 

2903 name = quoted_name or unquoted_name 

2904 yield name, list(self._find_cols_in_sig(cols)) 

2905 

2906 # we need to match inlines as well, as we seek to differentiate 

2907 # a UNIQUE constraint from a UNIQUE INDEX, even though these 

2908 # are kind of the same thing :) 

2909 for match in re.finditer(INLINE_UNIQUE_PATTERN, table_data, re.I): 

2910 cols = list( 

2911 self._find_cols_in_sig(match.group(1) or match.group(2)) 

2912 ) 

2913 yield None, cols 

2914 

2915 for name, cols in parse_uqs(): 

2916 sig = tuple(cols) 

2917 if sig in auto_index_by_sig: 

2918 auto_index_by_sig.pop(sig) 

2919 parsed_constraint = {"name": name, "column_names": cols} 

2920 unique_constraints.append(parsed_constraint) 

2921 # NOTE: auto_index_by_sig might not be empty here, 

2922 # the PRIMARY KEY may have an entry. 

2923 if unique_constraints: 

2924 return unique_constraints 

2925 else: 

2926 return ReflectionDefaults.unique_constraints() 

2927 

2928 @reflection.cache 

2929 def get_check_constraints(self, connection, table_name, schema=None, **kw): 

2930 table_data = self._get_table_sql( 

2931 connection, table_name, schema=schema, **kw 

2932 ) 

2933 

2934 # Extract CHECK constraints by properly handling balanced parentheses 

2935 # and avoiding false matches when CHECK/CONSTRAINT appear in table 

2936 # names. See #12924 for context. 

2937 # 

2938 # SQLite supports 4 identifier quote styles (see 

2939 # sqlite.org/lang_keywords.html): 

2940 # - Double quotes "..." (standard SQL) 

2941 # - Brackets [...] (MS Access/SQL Server compatibility) 

2942 # - Backticks `...` (MySQL compatibility) 

2943 # - Single quotes '...' (SQLite extension) 

2944 # 

2945 # NOTE: there is not currently a way to parse CHECK constraints that 

2946 # contain newlines as the approach here relies upon each individual 

2947 # CHECK constraint being on a single line by itself. This necessarily 

2948 # makes assumptions as to how the CREATE TABLE was emitted. 

2949 CHECK_PATTERN = re.compile( 

2950 r""" 

2951 (?<![A-Za-z0-9_]) # Negative lookbehind: ensure CHECK is not 

2952 # part of an identifier (e.g., table name 

2953 # like "tableCHECK") 

2954 

2955 (?: # Optional CONSTRAINT clause 

2956 CONSTRAINT\s+ 

2957 ( # Group 1: Constraint name (quoted or unquoted) 

2958 "(?:[^"]|"")+" # Double-quoted: "name" or "na""me" 

2959 |'(?:[^']|'')+' # Single-quoted: 'name' or 'na''me' 

2960 |\[(?:[^\]]|\]\])+\] # Bracket-quoted: [name] or [na]]me] 

2961 |`(?:[^`]|``)+` # Backtick-quoted: `name` or `na``me` 

2962 |\S+ # Unquoted: simple_name 

2963 ) 

2964 \s+ 

2965 )? 

2966 

2967 CHECK\s*\( # CHECK keyword followed by opening paren 

2968 """, 

2969 re.VERBOSE | re.IGNORECASE, 

2970 ) 

2971 cks = [] 

2972 

2973 for match in re.finditer(CHECK_PATTERN, table_data or ""): 

2974 constraint_name = match.group(1) 

2975 

2976 if constraint_name: 

2977 # Remove surrounding quotes if present 

2978 # Double quotes: "name" -> name 

2979 # Single quotes: 'name' -> name 

2980 # Brackets: [name] -> name 

2981 # Backticks: `name` -> name 

2982 constraint_name = re.sub( 

2983 r'^(["\'`])(.+)\1$|^\[(.+)\]$', 

2984 lambda m: m.group(2) or m.group(3), 

2985 constraint_name, 

2986 flags=re.DOTALL, 

2987 ) 

2988 

2989 # Find the matching closing parenthesis with quote-aware paren 

2990 # counting. ``match.end() - 1`` is the position of the ``(`` 

2991 # that opened the CHECK clause; ``match.end()`` is the first 

2992 # character of the constraint body. 

2993 close = util.find_matching_paren(table_data, match.end() - 1) 

2994 if close is not None: 

2995 sqltext = table_data[match.end() : close].strip() 

2996 cks.append({"sqltext": sqltext, "name": constraint_name}) 

2997 

2998 cks.sort(key=lambda d: d["name"] or "~") # sort None as last 

2999 if cks: 

3000 return cks 

3001 else: 

3002 return ReflectionDefaults.check_constraints() 

3003 

3004 @reflection.cache 

3005 def get_indexes(self, connection, table_name, schema=None, **kw): 

3006 pragma_indexes = self._get_table_pragma( 

3007 connection, "index_list", table_name, schema=schema 

3008 ) 

3009 indexes = [] 

3010 

3011 # regular expression to extract the filter predicate of a partial 

3012 # index. this could fail to extract the predicate correctly on 

3013 # indexes created like 

3014 # CREATE INDEX i ON t (col || ') where') WHERE col <> '' 

3015 # but as this function does not support expression-based indexes 

3016 # this case does not occur. 

3017 partial_pred_re = re.compile(r"\)\s+where\s+(.+)", re.IGNORECASE) 

3018 

3019 if schema: 

3020 schema_expr = "%s." % self.identifier_preparer.quote_identifier( 

3021 schema 

3022 ) 

3023 else: 

3024 schema_expr = "" 

3025 

3026 include_auto_indexes = kw.pop("include_auto_indexes", False) 

3027 for row in pragma_indexes: 

3028 # ignore implicit primary key index. 

3029 # https://www.mail-archive.com/sqlite-users@sqlite.org/msg30517.html 

3030 if not include_auto_indexes and row[1].startswith( 

3031 "sqlite_autoindex" 

3032 ): 

3033 continue 

3034 indexes.append( 

3035 dict( 

3036 name=row[1], 

3037 column_names=[], 

3038 unique=row[2], 

3039 dialect_options={}, 

3040 ) 

3041 ) 

3042 

3043 # check partial indexes 

3044 if len(row) >= 5 and row[4]: 

3045 s = ( 

3046 "SELECT sql FROM %(schema)ssqlite_master " 

3047 "WHERE name = ? " 

3048 "AND type = 'index'" % {"schema": schema_expr} 

3049 ) 

3050 rs = connection.exec_driver_sql(s, (row[1],)) 

3051 index_sql = rs.scalar() 

3052 predicate_match = partial_pred_re.search(index_sql) 

3053 if predicate_match is None: 

3054 # unless the regex is broken this case shouldn't happen 

3055 # because we know this is a partial index, so the 

3056 # definition sql should match the regex 

3057 util.warn( 

3058 "Failed to look up filter predicate of " 

3059 "partial index %s" % row[1] 

3060 ) 

3061 else: 

3062 predicate = predicate_match.group(1) 

3063 indexes[-1]["dialect_options"]["sqlite_where"] = text( 

3064 predicate 

3065 ) 

3066 

3067 # loop thru unique indexes to get the column names. 

3068 for idx in list(indexes): 

3069 pragma_index = self._get_table_pragma( 

3070 connection, "index_info", idx["name"], schema=schema 

3071 ) 

3072 

3073 for row in pragma_index: 

3074 if row[2] is None: 

3075 util.warn( 

3076 "Skipped unsupported reflection of " 

3077 "expression-based index %s" % idx["name"] 

3078 ) 

3079 indexes.remove(idx) 

3080 break 

3081 else: 

3082 idx["column_names"].append(row[2]) 

3083 

3084 indexes.sort(key=lambda d: d["name"] or "~") # sort None as last 

3085 if indexes: 

3086 return indexes 

3087 elif not self.has_table(connection, table_name, schema): 

3088 raise exc.NoSuchTableError( 

3089 f"{schema}.{table_name}" if schema else table_name 

3090 ) 

3091 else: 

3092 return ReflectionDefaults.indexes() 

3093 

3094 def _is_sys_table(self, table_name): 

3095 return table_name in { 

3096 "sqlite_schema", 

3097 "sqlite_master", 

3098 "sqlite_temp_schema", 

3099 "sqlite_temp_master", 

3100 } 

3101 

3102 @reflection.cache 

3103 def _get_table_sql(self, connection, table_name, schema=None, **kw): 

3104 if schema: 

3105 schema_expr = "%s." % ( 

3106 self.identifier_preparer.quote_identifier(schema) 

3107 ) 

3108 else: 

3109 schema_expr = "" 

3110 try: 

3111 s = ( 

3112 "SELECT sql FROM " 

3113 " (SELECT * FROM %(schema)ssqlite_master UNION ALL " 

3114 " SELECT * FROM %(schema)ssqlite_temp_master) " 

3115 "WHERE name = ? " 

3116 "AND type in ('table', 'view')" % {"schema": schema_expr} 

3117 ) 

3118 rs = connection.exec_driver_sql(s, (table_name,)) 

3119 except exc.DBAPIError: 

3120 s = ( 

3121 "SELECT sql FROM %(schema)ssqlite_master " 

3122 "WHERE name = ? " 

3123 "AND type in ('table', 'view')" % {"schema": schema_expr} 

3124 ) 

3125 rs = connection.exec_driver_sql(s, (table_name,)) 

3126 value = rs.scalar() 

3127 if value is None and not self._is_sys_table(table_name): 

3128 raise exc.NoSuchTableError(f"{schema_expr}{table_name}") 

3129 return value 

3130 

3131 def _get_table_pragma(self, connection, pragma, table_name, schema=None): 

3132 quote = self.identifier_preparer.quote_identifier 

3133 if schema is not None: 

3134 statements = [f"PRAGMA {quote(schema)}."] 

3135 else: 

3136 # because PRAGMA looks in all attached databases if no schema 

3137 # given, need to specify "main" schema, however since we want 

3138 # 'temp' tables in the same namespace as 'main', need to run 

3139 # the PRAGMA twice 

3140 statements = ["PRAGMA main.", "PRAGMA temp."] 

3141 

3142 qtable = quote(table_name) 

3143 for statement in statements: 

3144 statement = f"{statement}{pragma}({qtable})" 

3145 cursor = connection.exec_driver_sql(statement) 

3146 if not cursor._soft_closed: 

3147 # work around SQLite issue whereby cursor.description 

3148 # is blank when PRAGMA returns no rows: 

3149 # https://www.sqlite.org/cvstrac/tktview?tn=1884 

3150 result = cursor.fetchall() 

3151 else: 

3152 result = [] 

3153 if result: 

3154 return result 

3155 else: 

3156 return []