1
0
mirror of https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git synced 2025-08-14 00:25:46 +02:00

Проверка 09.02.2025

This commit is contained in:
MoonTestUse1
2025-02-09 01:11:49 +06:00
parent ce52f8a23a
commit 0aa3ef8fc2
5827 changed files with 14316 additions and 1906434 deletions

View File

@@ -1,5 +1,5 @@
# dialects/sqlite/__init__.py
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under

View File

@@ -1,5 +1,5 @@
# dialects/sqlite/aiosqlite.py
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
@@ -31,6 +31,7 @@ This dialect should normally be used only with the
:func:`_asyncio.create_async_engine` engine creation function::
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("sqlite+aiosqlite:///filename")
The URL passes through all arguments to the ``pysqlite`` driver, so all
@@ -58,12 +59,14 @@ The solution is similar to :ref:`pysqlite_serializable`. This is achieved by the
engine = create_async_engine("sqlite+aiosqlite:///myfile.db")
@event.listens_for(engine.sync_engine, "connect")
def do_connect(dbapi_connection, connection_record):
# disable aiosqlite's emitting of the BEGIN statement entirely.
# also stops it from emitting COMMIT before any DDL.
dbapi_connection.isolation_level = None
@event.listens_for(engine.sync_engine, "begin")
def do_begin(conn):
# emit our own BEGIN
@@ -75,9 +78,32 @@ The solution is similar to :ref:`pysqlite_serializable`. This is achieved by the
with the SQLite driver,
as this function necessarily will also alter the ".isolation_level" setting.
.. _aiosqlite_pooling:
Pooling Behavior
----------------
The SQLAlchemy ``aiosqlite`` DBAPI establishes the connection pool differently
based on the kind of SQLite database that's requested:
* When a ``:memory:`` SQLite database is specified, the dialect by default
will use :class:`.StaticPool`. This pool maintains a single
connection, so that all access to the engine
use the same ``:memory:`` database.
* When a file-based database is specified, the dialect will use
:class:`.AsyncAdaptedQueuePool` as the source of connections.
.. versionchanged:: 2.0.38
SQLite file database engines now use :class:`.AsyncAdaptedQueuePool` by default.
Previously, :class:`.NullPool` were used. The :class:`.NullPool` class
may be used by specifying it via the
:paramref:`_sa.create_engine.poolclass` parameter.
""" # noqa
import asyncio
from collections import deque
from functools import partial
from .base import SQLiteExecutionContext
@@ -113,10 +139,10 @@ class AsyncAdapt_aiosqlite_cursor:
self.arraysize = 1
self.rowcount = -1
self.description = None
self._rows = []
self._rows = deque()
def close(self):
self._rows[:] = []
self._rows.clear()
def execute(self, operation, parameters=None):
try:
@@ -132,7 +158,7 @@ class AsyncAdapt_aiosqlite_cursor:
self.lastrowid = self.rowcount = -1
if not self.server_side:
self._rows = self.await_(_cursor.fetchall())
self._rows = deque(self.await_(_cursor.fetchall()))
else:
self.description = None
self.lastrowid = _cursor.lastrowid
@@ -161,11 +187,11 @@ class AsyncAdapt_aiosqlite_cursor:
def __iter__(self):
while self._rows:
yield self._rows.pop(0)
yield self._rows.popleft()
def fetchone(self):
if self._rows:
return self._rows.pop(0)
return self._rows.popleft()
else:
return None
@@ -173,13 +199,12 @@ class AsyncAdapt_aiosqlite_cursor:
if size is None:
size = self.arraysize
retval = self._rows[0:size]
self._rows[:] = self._rows[size:]
return retval
rr = self._rows
return [rr.popleft() for _ in range(min(size, len(rr)))]
def fetchall(self):
retval = self._rows[:]
self._rows[:] = []
retval = list(self._rows)
self._rows.clear()
return retval
@@ -377,7 +402,7 @@ class SQLiteDialect_aiosqlite(SQLiteDialect_pysqlite):
@classmethod
def get_pool_class(cls, url):
if cls._is_url_file_db(url):
return pool.NullPool
return pool.AsyncAdaptedQueuePool
else:
return pool.StaticPool

View File

@@ -1,5 +1,5 @@
# dialects/sqlite/base.py
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
@@ -7,10 +7,9 @@
# mypy: ignore-errors
r"""
r'''
.. dialect:: sqlite
:name: SQLite
:full_support: 3.36.0
:normal_support: 3.12+
:best_effort: 3.7.16+
@@ -70,9 +69,12 @@ To specifically render the AUTOINCREMENT keyword on the primary key column
when rendering DDL, add the flag ``sqlite_autoincrement=True`` to the Table
construct::
Table('sometable', metadata,
Column('id', Integer, primary_key=True),
sqlite_autoincrement=True)
Table(
"sometable",
metadata,
Column("id", Integer, primary_key=True),
sqlite_autoincrement=True,
)
Allowing autoincrement behavior SQLAlchemy types other than Integer/INTEGER
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -92,8 +94,13 @@ One approach to achieve this is to use :class:`.Integer` on SQLite
only using :meth:`.TypeEngine.with_variant`::
table = Table(
"my_table", metadata,
Column("id", BigInteger().with_variant(Integer, "sqlite"), primary_key=True)
"my_table",
metadata,
Column(
"id",
BigInteger().with_variant(Integer, "sqlite"),
primary_key=True,
),
)
Another is to use a subclass of :class:`.BigInteger` that overrides its DDL
@@ -102,21 +109,23 @@ name to be ``INTEGER`` when compiled against SQLite::
from sqlalchemy import BigInteger
from sqlalchemy.ext.compiler import compiles
class SLBigInteger(BigInteger):
pass
@compiles(SLBigInteger, 'sqlite')
@compiles(SLBigInteger, "sqlite")
def bi_c(element, compiler, **kw):
return "INTEGER"
@compiles(SLBigInteger)
def bi_c(element, compiler, **kw):
return compiler.visit_BIGINT(element, **kw)
table = Table(
"my_table", metadata,
Column("id", SLBigInteger(), primary_key=True)
"my_table", metadata, Column("id", SLBigInteger(), primary_key=True)
)
.. seealso::
@@ -236,26 +245,24 @@ To specify an explicit ``RETURNING`` clause, use the
# INSERT..RETURNING
result = connection.execute(
table.insert().
values(name='foo').
returning(table.c.col1, table.c.col2)
table.insert().values(name="foo").returning(table.c.col1, table.c.col2)
)
print(result.all())
# UPDATE..RETURNING
result = connection.execute(
table.update().
where(table.c.name=='foo').
values(name='bar').
returning(table.c.col1, table.c.col2)
table.update()
.where(table.c.name == "foo")
.values(name="bar")
.returning(table.c.col1, table.c.col2)
)
print(result.all())
# DELETE..RETURNING
result = connection.execute(
table.delete().
where(table.c.name=='foo').
returning(table.c.col1, table.c.col2)
table.delete()
.where(table.c.name == "foo")
.returning(table.c.col1, table.c.col2)
)
print(result.all())
@@ -318,6 +325,7 @@ new connections through the usage of events::
from sqlalchemy.engine import Engine
from sqlalchemy import event
@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
@@ -380,13 +388,16 @@ ABORT, FAIL, IGNORE, and REPLACE. For example, to add a UNIQUE constraint
that specifies the IGNORE algorithm::
some_table = Table(
'some_table', metadata,
Column('id', Integer, primary_key=True),
Column('data', Integer),
UniqueConstraint('id', 'data', sqlite_on_conflict='IGNORE')
"some_table",
metadata,
Column("id", Integer, primary_key=True),
Column("data", Integer),
UniqueConstraint("id", "data", sqlite_on_conflict="IGNORE"),
)
The above renders CREATE TABLE DDL as::
The above renders CREATE TABLE DDL as:
.. sourcecode:: sql
CREATE TABLE some_table (
id INTEGER NOT NULL,
@@ -403,13 +414,17 @@ be added to the :class:`_schema.Column` as well, which will be added to the
UNIQUE constraint in the DDL::
some_table = Table(
'some_table', metadata,
Column('id', Integer, primary_key=True),
Column('data', Integer, unique=True,
sqlite_on_conflict_unique='IGNORE')
"some_table",
metadata,
Column("id", Integer, primary_key=True),
Column(
"data", Integer, unique=True, sqlite_on_conflict_unique="IGNORE"
),
)
rendering::
rendering:
.. sourcecode:: sql
CREATE TABLE some_table (
id INTEGER NOT NULL,
@@ -422,13 +437,17 @@ To apply the FAIL algorithm for a NOT NULL constraint,
``sqlite_on_conflict_not_null`` is used::
some_table = Table(
'some_table', metadata,
Column('id', Integer, primary_key=True),
Column('data', Integer, nullable=False,
sqlite_on_conflict_not_null='FAIL')
"some_table",
metadata,
Column("id", Integer, primary_key=True),
Column(
"data", Integer, nullable=False, sqlite_on_conflict_not_null="FAIL"
),
)
this renders the column inline ON CONFLICT phrase::
this renders the column inline ON CONFLICT phrase:
.. sourcecode:: sql
CREATE TABLE some_table (
id INTEGER NOT NULL,
@@ -440,13 +459,20 @@ this renders the column inline ON CONFLICT phrase::
Similarly, for an inline primary key, use ``sqlite_on_conflict_primary_key``::
some_table = Table(
'some_table', metadata,
Column('id', Integer, primary_key=True,
sqlite_on_conflict_primary_key='FAIL')
"some_table",
metadata,
Column(
"id",
Integer,
primary_key=True,
sqlite_on_conflict_primary_key="FAIL",
),
)
SQLAlchemy renders the PRIMARY KEY constraint separately, so the conflict
resolution algorithm is applied to the constraint itself::
resolution algorithm is applied to the constraint itself:
.. sourcecode:: sql
CREATE TABLE some_table (
id INTEGER NOT NULL,
@@ -456,7 +482,7 @@ resolution algorithm is applied to the constraint itself::
.. _sqlite_on_conflict_insert:
INSERT...ON CONFLICT (Upsert)
-----------------------------------
-----------------------------
.. seealso:: This section describes the :term:`DML` version of "ON CONFLICT" for
SQLite, which occurs within an INSERT statement. For "ON CONFLICT" as
@@ -484,21 +510,18 @@ and :meth:`_sqlite.Insert.on_conflict_do_nothing`:
>>> from sqlalchemy.dialects.sqlite import insert
>>> insert_stmt = insert(my_table).values(
... id='some_existing_id',
... data='inserted value')
... id="some_existing_id", data="inserted value"
... )
>>> do_update_stmt = insert_stmt.on_conflict_do_update(
... index_elements=['id'],
... set_=dict(data='updated value')
... index_elements=["id"], set_=dict(data="updated value")
... )
>>> print(do_update_stmt)
{printsql}INSERT INTO my_table (id, data) VALUES (?, ?)
ON CONFLICT (id) DO UPDATE SET data = ?{stop}
>>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing(
... index_elements=['id']
... )
>>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing(index_elements=["id"])
>>> print(do_nothing_stmt)
{printsql}INSERT INTO my_table (id, data) VALUES (?, ?)
@@ -529,13 +552,13 @@ Both methods supply the "target" of the conflict using column inference:
.. sourcecode:: pycon+sql
>>> stmt = insert(my_table).values(user_email='a@b.com', data='inserted data')
>>> stmt = insert(my_table).values(user_email="a@b.com", data="inserted data")
>>> do_update_stmt = stmt.on_conflict_do_update(
... index_elements=[my_table.c.user_email],
... index_where=my_table.c.user_email.like('%@gmail.com'),
... set_=dict(data=stmt.excluded.data)
... )
... index_where=my_table.c.user_email.like("%@gmail.com"),
... set_=dict(data=stmt.excluded.data),
... )
>>> print(do_update_stmt)
{printsql}INSERT INTO my_table (data, user_email) VALUES (?, ?)
@@ -555,11 +578,10 @@ for UPDATE:
.. sourcecode:: pycon+sql
>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = insert(my_table).values(id="some_id", data="inserted value")
>>> do_update_stmt = stmt.on_conflict_do_update(
... index_elements=['id'],
... set_=dict(data='updated value')
... index_elements=["id"], set_=dict(data="updated value")
... )
>>> print(do_update_stmt)
@@ -587,14 +609,12 @@ would have been inserted had the constraint not failed:
.. sourcecode:: pycon+sql
>>> stmt = insert(my_table).values(
... id='some_id',
... data='inserted value',
... author='jlh'
... id="some_id", data="inserted value", author="jlh"
... )
>>> do_update_stmt = stmt.on_conflict_do_update(
... index_elements=['id'],
... set_=dict(data='updated value', author=stmt.excluded.author)
... index_elements=["id"],
... set_=dict(data="updated value", author=stmt.excluded.author),
... )
>>> print(do_update_stmt)
@@ -611,15 +631,13 @@ parameter, which will limit those rows which receive an UPDATE:
.. sourcecode:: pycon+sql
>>> stmt = insert(my_table).values(
... id='some_id',
... data='inserted value',
... author='jlh'
... id="some_id", data="inserted value", author="jlh"
... )
>>> on_update_stmt = stmt.on_conflict_do_update(
... index_elements=['id'],
... set_=dict(data='updated value', author=stmt.excluded.author),
... where=(my_table.c.status == 2)
... index_elements=["id"],
... set_=dict(data="updated value", author=stmt.excluded.author),
... where=(my_table.c.status == 2),
... )
>>> print(on_update_stmt)
{printsql}INSERT INTO my_table (id, data, author) VALUES (?, ?, ?)
@@ -636,8 +654,8 @@ using the :meth:`_sqlite.Insert.on_conflict_do_nothing` method:
.. sourcecode:: pycon+sql
>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = stmt.on_conflict_do_nothing(index_elements=['id'])
>>> stmt = insert(my_table).values(id="some_id", data="inserted value")
>>> stmt = stmt.on_conflict_do_nothing(index_elements=["id"])
>>> print(stmt)
{printsql}INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT (id) DO NOTHING
@@ -648,7 +666,7 @@ occurs:
.. sourcecode:: pycon+sql
>>> stmt = insert(my_table).values(id='some_id', data='inserted value')
>>> stmt = insert(my_table).values(id="some_id", data="inserted value")
>>> stmt = stmt.on_conflict_do_nothing()
>>> print(stmt)
{printsql}INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT DO NOTHING
@@ -708,11 +726,16 @@ Partial Indexes
A partial index, e.g. one which uses a WHERE clause, can be specified
with the DDL system using the argument ``sqlite_where``::
tbl = Table('testtbl', m, Column('data', Integer))
idx = Index('test_idx1', tbl.c.data,
sqlite_where=and_(tbl.c.data > 5, tbl.c.data < 10))
tbl = Table("testtbl", m, Column("data", Integer))
idx = Index(
"test_idx1",
tbl.c.data,
sqlite_where=and_(tbl.c.data > 5, tbl.c.data < 10),
)
The index will be rendered at create time as::
The index will be rendered at create time as:
.. sourcecode:: sql
CREATE INDEX test_idx1 ON testtbl (data)
WHERE data > 5 AND data < 10
@@ -732,7 +755,11 @@ The bug, entirely outside of SQLAlchemy, can be illustrated thusly::
import sqlite3
assert sqlite3.sqlite_version_info < (3, 10, 0), "bug is fixed in this version"
assert sqlite3.sqlite_version_info < (
3,
10,
0,
), "bug is fixed in this version"
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
@@ -742,17 +769,22 @@ The bug, entirely outside of SQLAlchemy, can be illustrated thusly::
cursor.execute("insert into x (a, b) values (2, 2)")
cursor.execute("select x.a, x.b from x")
assert [c[0] for c in cursor.description] == ['a', 'b']
assert [c[0] for c in cursor.description] == ["a", "b"]
cursor.execute('''
cursor.execute(
"""
select x.a, x.b from x where a=1
union
select x.a, x.b from x where a=2
''')
assert [c[0] for c in cursor.description] == ['a', 'b'], \
[c[0] for c in cursor.description]
"""
)
assert [c[0] for c in cursor.description] == ["a", "b"], [
c[0] for c in cursor.description
]
The second assertion fails::
The second assertion fails:
.. sourcecode:: text
Traceback (most recent call last):
File "test.py", line 19, in <module>
@@ -780,11 +812,13 @@ to filter these out::
result = conn.exec_driver_sql("select x.a, x.b from x")
assert result.keys() == ["a", "b"]
result = conn.exec_driver_sql('''
result = conn.exec_driver_sql(
"""
select x.a, x.b from x where a=1
union
select x.a, x.b from x where a=2
''')
"""
)
assert result.keys() == ["a", "b"]
Note that above, even though SQLAlchemy filters out the dots, *both
@@ -808,16 +842,20 @@ contain dots, and the functionality of :meth:`_engine.CursorResult.keys` and
the ``sqlite_raw_colnames`` execution option may be provided, either on a
per-:class:`_engine.Connection` basis::
result = conn.execution_options(sqlite_raw_colnames=True).exec_driver_sql('''
result = conn.execution_options(sqlite_raw_colnames=True).exec_driver_sql(
"""
select x.a, x.b from x where a=1
union
select x.a, x.b from x where a=2
''')
"""
)
assert result.keys() == ["x.a", "x.b"]
or on a per-:class:`_engine.Engine` basis::
engine = create_engine("sqlite://", execution_options={"sqlite_raw_colnames": True})
engine = create_engine(
"sqlite://", execution_options={"sqlite_raw_colnames": True}
)
When using the per-:class:`_engine.Engine` execution option, note that
**Core and ORM queries that use UNION may not function properly**.
@@ -832,12 +870,18 @@ dialect in conjunction with the :class:`_schema.Table` construct:
Table("some_table", metadata, ..., sqlite_with_rowid=False)
*
``STRICT``::
Table("some_table", metadata, ..., sqlite_strict=True)
.. versionadded:: 2.0.37
.. seealso::
`SQLite CREATE TABLE options
<https://www.sqlite.org/lang_createtable.html>`_
.. _sqlite_include_internal:
Reflecting internal schema tables
@@ -866,7 +910,7 @@ passed to methods such as :meth:`_schema.MetaData.reflect` or
`SQLite Internal Schema Objects <https://www.sqlite.org/fileformat2.html#intschema>`_ - in the SQLite
documentation.
""" # noqa
''' # noqa
from __future__ import annotations
import datetime
@@ -980,7 +1024,9 @@ class DATETIME(_DateTimeMixin, sqltypes.DateTime):
"%(year)04d-%(month)02d-%(day)02d %(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
e.g.::
e.g.:
.. sourcecode:: text
2021-03-15 12:05:57.105542
@@ -996,9 +1042,11 @@ class DATETIME(_DateTimeMixin, sqltypes.DateTime):
import re
from sqlalchemy.dialects.sqlite import DATETIME
dt = DATETIME(storage_format="%(year)04d/%(month)02d/%(day)02d "
"%(hour)02d:%(minute)02d:%(second)02d",
regexp=r"(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)"
dt = DATETIME(
storage_format=(
"%(year)04d/%(month)02d/%(day)02d %(hour)02d:%(minute)02d:%(second)02d"
),
regexp=r"(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)",
)
:param storage_format: format string which will be applied to the dict
@@ -1088,7 +1136,9 @@ class DATE(_DateTimeMixin, sqltypes.Date):
"%(year)04d-%(month)02d-%(day)02d"
e.g.::
e.g.:
.. sourcecode:: text
2011-03-15
@@ -1106,9 +1156,9 @@ class DATE(_DateTimeMixin, sqltypes.Date):
from sqlalchemy.dialects.sqlite import DATE
d = DATE(
storage_format="%(month)02d/%(day)02d/%(year)04d",
regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)")
)
storage_format="%(month)02d/%(day)02d/%(year)04d",
regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)"),
)
:param storage_format: format string which will be applied to the
dict with keys year, month, and day.
@@ -1162,7 +1212,9 @@ class TIME(_DateTimeMixin, sqltypes.Time):
"%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
e.g.::
e.g.:
.. sourcecode:: text
12:05:57.10558
@@ -1178,9 +1230,9 @@ class TIME(_DateTimeMixin, sqltypes.Time):
import re
from sqlalchemy.dialects.sqlite import TIME
t = TIME(storage_format="%(hour)02d-%(minute)02d-"
"%(second)02d-%(microsecond)06d",
regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?")
t = TIME(
storage_format="%(hour)02d-%(minute)02d-%(second)02d-%(microsecond)06d",
regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?"),
)
:param storage_format: format string which will be applied to the dict
@@ -1429,9 +1481,7 @@ class SQLiteCompiler(compiler.SQLCompiler):
return self._generate_generic_binary(binary, " NOT REGEXP ", **kw)
def _on_conflict_target(self, clause, **kw):
if clause.constraint_target is not None:
target_text = "(%s)" % clause.constraint_target
elif clause.inferred_target_elements is not None:
if clause.inferred_target_elements is not None:
target_text = "(%s)" % ", ".join(
(
self.preparer.quote(c)
@@ -1445,7 +1495,7 @@ class SQLiteCompiler(compiler.SQLCompiler):
clause.inferred_target_whereclause,
include_table=False,
use_schema=False,
literal_binds=True,
literal_execute=True,
)
else:
@@ -1528,6 +1578,13 @@ class SQLiteCompiler(compiler.SQLCompiler):
return "ON CONFLICT %s DO UPDATE SET %s" % (target_text, action_text)
def visit_bitwise_xor_op_binary(self, binary, operator, **kw):
# sqlite has no xor. Use "a XOR b" = "(a | b) - (a & b)".
kw["eager_grouping"] = True
or_ = self._generate_generic_binary(binary, " | ", **kw)
and_ = self._generate_generic_binary(binary, " & ", **kw)
return f"({or_} - {and_})"
class SQLiteDDLCompiler(compiler.DDLCompiler):
def get_column_specification(self, column, **kwargs):
@@ -1701,9 +1758,12 @@ class SQLiteDDLCompiler(compiler.DDLCompiler):
return text
def post_create_table(self, table):
text = ""
if table.dialect_options["sqlite"]["with_rowid"] is False:
return "\n WITHOUT ROWID"
return ""
text += "\n WITHOUT ROWID"
if table.dialect_options["sqlite"]["strict"] is True:
text += "\n STRICT"
return text
class SQLiteTypeCompiler(compiler.GenericTypeCompiler):
@@ -1938,6 +1998,7 @@ class SQLiteDialect(default.DefaultDialect):
{
"autoincrement": False,
"with_rowid": True,
"strict": False,
},
),
(sa_schema.Index, {"where": None}),
@@ -2231,6 +2292,14 @@ class SQLiteDialect(default.DefaultDialect):
tablesql = self._get_table_sql(
connection, table_name, schema, **kw
)
# remove create table
match = re.match(
r"create table .*?\((.*)\)$",
tablesql.strip(),
re.DOTALL | re.IGNORECASE,
)
assert match, f"create table not found in {tablesql}"
tablesql = match.group(1).strip()
columns.append(
self._get_column_info(
@@ -2285,7 +2354,10 @@ class SQLiteDialect(default.DefaultDialect):
if generated:
sqltext = ""
if tablesql:
pattern = r"[^,]*\s+AS\s+\(([^,]*)\)\s*(?:virtual|stored)?"
pattern = (
r"[^,]*\s+GENERATED\s+ALWAYS\s+AS"
r"\s+\((.*)\)\s*(?:virtual|stored)?"
)
match = re.search(
re.escape(name) + pattern, tablesql, re.IGNORECASE
)
@@ -2570,8 +2642,8 @@ class SQLiteDialect(default.DefaultDialect):
return
UNIQUE_PATTERN = r'(?:CONSTRAINT "?(.+?)"? +)?UNIQUE *\((.+?)\)'
INLINE_UNIQUE_PATTERN = (
r'(?:(".+?")|(?:[\[`])?([a-z0-9_]+)(?:[\]`])?) '
r"+[a-z0-9_ ]+? +UNIQUE"
r'(?:(".+?")|(?:[\[`])?([a-z0-9_]+)(?:[\]`])?)[\t ]'
r"+[a-z0-9_ ]+?[\t ]+UNIQUE"
)
for match in re.finditer(UNIQUE_PATTERN, table_data, re.I):
@@ -2606,15 +2678,21 @@ class SQLiteDialect(default.DefaultDialect):
connection, table_name, schema=schema, **kw
)
CHECK_PATTERN = r"(?:CONSTRAINT (.+) +)?" r"CHECK *\( *(.+) *\),? *"
cks = []
# NOTE: we aren't using re.S here because we actually are
# taking advantage of each CHECK constraint being all on one
# line in the table definition in order to delineate. This
# NOTE NOTE NOTE
# DO NOT CHANGE THIS REGULAR EXPRESSION. There is no known way
# to parse CHECK constraints that contain newlines themselves using
# regular expressions, and the approach here relies upon each
# individual
# CHECK constraint being on a single line by itself. This
# necessarily makes assumptions as to how the CREATE TABLE
# was emitted.
# was emitted. A more comprehensive DDL parsing solution would be
# needed to improve upon the current situation. See #11840 for
# background
CHECK_PATTERN = r"(?:CONSTRAINT (.+) +)?CHECK *\( *(.+) *\),? *"
cks = []
for match in re.finditer(CHECK_PATTERN, table_data or "", re.I):
name = match.group(1)
if name:

View File

@@ -1,5 +1,5 @@
# dialects/sqlite/dml.py
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
@@ -7,6 +7,10 @@
from __future__ import annotations
from typing import Any
from typing import List
from typing import Optional
from typing import Tuple
from typing import Union
from .._typing import _OnConflictIndexElementsT
from .._typing import _OnConflictIndexWhereT
@@ -15,6 +19,7 @@ from .._typing import _OnConflictWhereT
from ... import util
from ...sql import coercions
from ...sql import roles
from ...sql import schema
from ...sql._typing import _DMLTableArgument
from ...sql.base import _exclusive_against
from ...sql.base import _generative
@@ -22,7 +27,9 @@ from ...sql.base import ColumnCollection
from ...sql.base import ReadOnlyColumnCollection
from ...sql.dml import Insert as StandardInsert
from ...sql.elements import ClauseElement
from ...sql.elements import ColumnElement
from ...sql.elements import KeyedColumnElement
from ...sql.elements import TextClause
from ...sql.expression import alias
from ...util.typing import Self
@@ -141,11 +148,10 @@ class Insert(StandardInsert):
:paramref:`.Insert.on_conflict_do_update.set_` dictionary.
:param where:
Optional argument. If present, can be a literal SQL
string or an acceptable expression for a ``WHERE`` clause
that restricts the rows affected by ``DO UPDATE SET``. Rows
not meeting the ``WHERE`` condition will not be updated
(effectively a ``DO NOTHING`` for those rows).
Optional argument. An expression object representing a ``WHERE``
clause that restricts the rows affected by ``DO UPDATE SET``. Rows not
meeting the ``WHERE`` condition will not be updated (effectively a
``DO NOTHING`` for those rows).
"""
@@ -184,9 +190,10 @@ class Insert(StandardInsert):
class OnConflictClause(ClauseElement):
stringify_dialect = "sqlite"
constraint_target: None
inferred_target_elements: _OnConflictIndexElementsT
inferred_target_whereclause: _OnConflictIndexWhereT
inferred_target_elements: Optional[List[Union[str, schema.Column[Any]]]]
inferred_target_whereclause: Optional[
Union[ColumnElement[Any], TextClause]
]
def __init__(
self,
@@ -194,11 +201,20 @@ class OnConflictClause(ClauseElement):
index_where: _OnConflictIndexWhereT = None,
):
if index_elements is not None:
self.constraint_target = None
self.inferred_target_elements = index_elements
self.inferred_target_whereclause = index_where
self.inferred_target_elements = [
coercions.expect(roles.DDLConstraintColumnRole, column)
for column in index_elements
]
self.inferred_target_whereclause = (
coercions.expect(
roles.WhereHavingRole,
index_where,
)
if index_where is not None
else None
)
else:
self.constraint_target = self.inferred_target_elements = (
self.inferred_target_elements = (
self.inferred_target_whereclause
) = None
@@ -210,6 +226,9 @@ class OnConflictDoNothing(OnConflictClause):
class OnConflictDoUpdate(OnConflictClause):
__visit_name__ = "on_conflict_do_update"
update_values_to_set: List[Tuple[Union[schema.Column[Any], str], Any]]
update_whereclause: Optional[ColumnElement[Any]]
def __init__(
self,
index_elements: _OnConflictIndexElementsT = None,
@@ -237,4 +256,8 @@ class OnConflictDoUpdate(OnConflictClause):
(coercions.expect(roles.DMLColumnRole, key), value)
for key, value in set_.items()
]
self.update_whereclause = where
self.update_whereclause = (
coercions.expect(roles.WhereHavingRole, where)
if where is not None
else None
)

View File

@@ -1,5 +1,5 @@
# dialects/sqlite/json.py
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under

View File

@@ -1,5 +1,5 @@
# dialects/sqlite/provision.py
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under

View File

@@ -1,5 +1,5 @@
# dialects/sqlite/pysqlcipher.py
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
@@ -39,7 +39,7 @@ Current dialect selection logic is:
e = create_engine(
"sqlite+pysqlcipher://:password@/dbname.db",
module=sqlcipher_compatible_driver
module=sqlcipher_compatible_driver,
)
These drivers make use of the SQLCipher engine. This system essentially
@@ -55,12 +55,12 @@ The format of the connect string is in every way the same as that
of the :mod:`~sqlalchemy.dialects.sqlite.pysqlite` driver, except that the
"password" field is now accepted, which should contain a passphrase::
e = create_engine('sqlite+pysqlcipher://:testing@/foo.db')
e = create_engine("sqlite+pysqlcipher://:testing@/foo.db")
For an absolute file path, two leading slashes should be used for the
database name::
e = create_engine('sqlite+pysqlcipher://:testing@//path/to/foo.db')
e = create_engine("sqlite+pysqlcipher://:testing@//path/to/foo.db")
A selection of additional encryption-related pragmas supported by SQLCipher
as documented at https://www.zetetic.net/sqlcipher/sqlcipher-api/ can be passed
@@ -68,7 +68,9 @@ in the query string, and will result in that PRAGMA being called for each
new connection. Currently, ``cipher``, ``kdf_iter``
``cipher_page_size`` and ``cipher_use_hmac`` are supported::
e = create_engine('sqlite+pysqlcipher://:testing@/foo.db?cipher=aes-256-cfb&kdf_iter=64000')
e = create_engine(
"sqlite+pysqlcipher://:testing@/foo.db?cipher=aes-256-cfb&kdf_iter=64000"
)
.. warning:: Previous versions of sqlalchemy did not take into consideration
the encryption-related pragmas passed in the url string, that were silently

View File

@@ -1,5 +1,5 @@
# dialects/sqlite/pysqlite.py
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
@@ -28,7 +28,9 @@ Connect Strings
---------------
The file specification for the SQLite database is taken as the "database"
portion of the URL. Note that the format of a SQLAlchemy url is::
portion of the URL. Note that the format of a SQLAlchemy url is:
.. sourcecode:: text
driver://user:pass@host/database
@@ -37,25 +39,28 @@ the **right** of the third slash. So connecting to a relative filepath
looks like::
# relative path
e = create_engine('sqlite:///path/to/database.db')
e = create_engine("sqlite:///path/to/database.db")
An absolute path, which is denoted by starting with a slash, means you
need **four** slashes::
# absolute path
e = create_engine('sqlite:////path/to/database.db')
e = create_engine("sqlite:////path/to/database.db")
To use a Windows path, regular drive specifications and backslashes can be
used. Double backslashes are probably needed::
# absolute path on Windows
e = create_engine('sqlite:///C:\\path\\to\\database.db')
e = create_engine("sqlite:///C:\\path\\to\\database.db")
The sqlite ``:memory:`` identifier is the default if no filepath is
present. Specify ``sqlite://`` and nothing else::
To use sqlite ``:memory:`` database specify it as the filename using
``sqlite:///:memory:``. It's also the default if no filepath is
present, specifying only ``sqlite://`` and nothing else::
# in-memory database
e = create_engine('sqlite://')
# in-memory database (note three slashes)
e = create_engine("sqlite:///:memory:")
# also in-memory database
e2 = create_engine("sqlite://")
.. _pysqlite_uri_connections:
@@ -95,7 +100,9 @@ Above, the pysqlite / sqlite3 DBAPI would be passed arguments as::
sqlite3.connect(
"file:path/to/database?mode=ro&nolock=1",
check_same_thread=True, timeout=10, uri=True
check_same_thread=True,
timeout=10,
uri=True,
)
Regarding future parameters added to either the Python or native drivers. new
@@ -141,8 +148,11 @@ as follows::
def regexp(a, b):
return re.search(a, b) is not None
sqlite_connection.create_function(
"regexp", 2, regexp,
"regexp",
2,
regexp,
)
There is currently no support for regular expression flags as a separate
@@ -183,10 +193,12 @@ Keeping in mind that pysqlite's parsing option is not recommended,
nor should be necessary, for use with SQLAlchemy, usage of PARSE_DECLTYPES
can be forced if one configures "native_datetime=True" on create_engine()::
engine = create_engine('sqlite://',
connect_args={'detect_types':
sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES},
native_datetime=True
engine = create_engine(
"sqlite://",
connect_args={
"detect_types": sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES
},
native_datetime=True,
)
With this flag enabled, the DATE and TIMESTAMP types (but note - not the
@@ -241,6 +253,7 @@ Pooling may be disabled for a file based database by specifying the
parameter::
from sqlalchemy import NullPool
engine = create_engine("sqlite:///myfile.db", poolclass=NullPool)
It's been observed that the :class:`.NullPool` implementation incurs an
@@ -260,9 +273,12 @@ globally, and the ``check_same_thread`` flag can be passed to Pysqlite
as ``False``::
from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite://',
connect_args={'check_same_thread':False},
poolclass=StaticPool)
engine = create_engine(
"sqlite://",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
Note that using a ``:memory:`` database in multiple threads requires a recent
version of SQLite.
@@ -281,14 +297,14 @@ needed within multiple threads for this case::
# maintain the same connection per thread
from sqlalchemy.pool import SingletonThreadPool
engine = create_engine('sqlite:///mydb.db',
poolclass=SingletonThreadPool)
engine = create_engine("sqlite:///mydb.db", poolclass=SingletonThreadPool)
# maintain the same connection across all threads
from sqlalchemy.pool import StaticPool
engine = create_engine('sqlite:///mydb.db',
poolclass=StaticPool)
engine = create_engine("sqlite:///mydb.db", poolclass=StaticPool)
Note that :class:`.SingletonThreadPool` should be configured for the number
of threads that are to be used; beyond that number, connections will be
@@ -317,13 +333,14 @@ same column, use a custom type that will check each row individually::
from sqlalchemy import String
from sqlalchemy import TypeDecorator
class MixedBinary(TypeDecorator):
impl = String
cache_ok = True
def process_result_value(self, value, dialect):
if isinstance(value, str):
value = bytes(value, 'utf-8')
value = bytes(value, "utf-8")
elif value is not None:
value = bytes(value)
@@ -364,12 +381,14 @@ ourselves. This is achieved using two event listeners::
engine = create_engine("sqlite:///myfile.db")
@event.listens_for(engine, "connect")
def do_connect(dbapi_connection, connection_record):
# disable pysqlite's emitting of the BEGIN statement entirely.
# also stops it from emitting COMMIT before any DDL.
dbapi_connection.isolation_level = None
@event.listens_for(engine, "begin")
def do_begin(conn):
# emit our own BEGIN
@@ -439,7 +458,6 @@ connection when it is created. That is accomplished with an event listener::
with engine.connect() as conn:
print(conn.scalar(text("SELECT UDF()")))
""" # noqa
import math