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 []