SQLAlchemy 2.0.42__py3-none-any.whl → 2.0.43__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlalchemy/__init__.py +1 -1
- sqlalchemy/connectors/pyodbc.py +5 -0
- sqlalchemy/dialects/mysql/aiomysql.py +3 -0
- sqlalchemy/dialects/mysql/asyncmy.py +3 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +3 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +3 -0
- sqlalchemy/dialects/mysql/mysqldb.py +3 -0
- sqlalchemy/dialects/oracle/__init__.py +4 -0
- sqlalchemy/dialects/oracle/base.py +79 -24
- sqlalchemy/dialects/oracle/cx_oracle.py +3 -0
- sqlalchemy/dialects/oracle/vector.py +104 -6
- sqlalchemy/dialects/postgresql/_psycopg_common.py +3 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +3 -0
- sqlalchemy/dialects/postgresql/pg8000.py +3 -0
- sqlalchemy/dialects/sqlite/base.py +8 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +3 -0
- sqlalchemy/engine/base.py +11 -5
- sqlalchemy/engine/create.py +15 -0
- sqlalchemy/engine/default.py +7 -0
- sqlalchemy/engine/interfaces.py +32 -0
- sqlalchemy/engine/result.py +1 -1
- sqlalchemy/orm/descriptor_props.py +5 -1
- sqlalchemy/orm/loading.py +13 -9
- sqlalchemy/orm/persistence.py +8 -2
- sqlalchemy/orm/properties.py +38 -15
- sqlalchemy/sql/functions.py +1 -3
- sqlalchemy/testing/requirements.py +7 -0
- sqlalchemy/testing/suite/test_dialect.py +37 -1
- {sqlalchemy-2.0.42.dist-info → sqlalchemy-2.0.43.dist-info}/METADATA +1 -1
- {sqlalchemy-2.0.42.dist-info → sqlalchemy-2.0.43.dist-info}/RECORD +33 -33
- {sqlalchemy-2.0.42.dist-info → sqlalchemy-2.0.43.dist-info}/WHEEL +0 -0
- {sqlalchemy-2.0.42.dist-info → sqlalchemy-2.0.43.dist-info}/licenses/LICENSE +0 -0
- {sqlalchemy-2.0.42.dist-info → sqlalchemy-2.0.43.dist-info}/top_level.txt +0 -0
sqlalchemy/__init__.py
CHANGED
sqlalchemy/connectors/pyodbc.py
CHANGED
|
@@ -243,3 +243,8 @@ class PyODBCConnector(Connector):
|
|
|
243
243
|
else:
|
|
244
244
|
dbapi_connection.autocommit = False
|
|
245
245
|
super().set_isolation_level(dbapi_connection, level)
|
|
246
|
+
|
|
247
|
+
def detect_autocommit_setting(
|
|
248
|
+
self, dbapi_conn: interfaces.DBAPIConnection
|
|
249
|
+
) -> bool:
|
|
250
|
+
return bool(dbapi_conn.autocommit)
|
|
@@ -98,6 +98,9 @@ class AsyncAdapt_aiomysql_connection(AsyncAdapt_dbapi_connection):
|
|
|
98
98
|
def autocommit(self, value: Any) -> None:
|
|
99
99
|
self.await_(self._connection.autocommit(value))
|
|
100
100
|
|
|
101
|
+
def get_autocommit(self) -> bool:
|
|
102
|
+
return self._connection.get_autocommit() # type: ignore
|
|
103
|
+
|
|
101
104
|
def terminate(self) -> None:
|
|
102
105
|
# it's not awaitable.
|
|
103
106
|
self._connection.close()
|
|
@@ -104,6 +104,9 @@ class AsyncAdapt_asyncmy_connection(AsyncAdapt_dbapi_connection):
|
|
|
104
104
|
def autocommit(self, value: Any) -> None:
|
|
105
105
|
self.await_(self._connection.autocommit(value))
|
|
106
106
|
|
|
107
|
+
def get_autocommit(self) -> bool:
|
|
108
|
+
return self._connection.get_autocommit() # type: ignore
|
|
109
|
+
|
|
107
110
|
def terminate(self) -> None:
|
|
108
111
|
# it's not awaitable.
|
|
109
112
|
self._connection.close()
|
|
@@ -253,6 +253,9 @@ class MySQLDialect_mariadbconnector(MySQLDialect):
|
|
|
253
253
|
"AUTOCOMMIT",
|
|
254
254
|
)
|
|
255
255
|
|
|
256
|
+
def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
|
|
257
|
+
return bool(dbapi_conn.autocommit)
|
|
258
|
+
|
|
256
259
|
def set_isolation_level(
|
|
257
260
|
self, dbapi_connection: DBAPIConnection, level: IsolationLevel
|
|
258
261
|
) -> None:
|
|
@@ -277,6 +277,9 @@ class MySQLDialect_mysqlconnector(MySQLDialect):
|
|
|
277
277
|
"AUTOCOMMIT",
|
|
278
278
|
)
|
|
279
279
|
|
|
280
|
+
def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
|
|
281
|
+
return bool(dbapi_conn.autocommit)
|
|
282
|
+
|
|
280
283
|
def set_isolation_level(
|
|
281
284
|
self, dbapi_connection: DBAPIConnection, level: IsolationLevel
|
|
282
285
|
) -> None:
|
|
@@ -298,6 +298,9 @@ class MySQLDialect_mysqldb(MySQLDialect):
|
|
|
298
298
|
"AUTOCOMMIT",
|
|
299
299
|
)
|
|
300
300
|
|
|
301
|
+
def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
|
|
302
|
+
return dbapi_conn.get_autocommit() # type: ignore[no-any-return]
|
|
303
|
+
|
|
301
304
|
def set_isolation_level(
|
|
302
305
|
self, dbapi_connection: DBAPIConnection, level: IsolationLevel
|
|
303
306
|
) -> None:
|
|
@@ -35,8 +35,10 @@ from .base import VARCHAR2
|
|
|
35
35
|
from .base import VECTOR
|
|
36
36
|
from .base import VectorIndexConfig
|
|
37
37
|
from .base import VectorIndexType
|
|
38
|
+
from .vector import SparseVector
|
|
38
39
|
from .vector import VectorDistanceType
|
|
39
40
|
from .vector import VectorStorageFormat
|
|
41
|
+
from .vector import VectorStorageType
|
|
40
42
|
|
|
41
43
|
# Alias oracledb also as oracledb_async
|
|
42
44
|
oracledb_async = type(
|
|
@@ -74,4 +76,6 @@ __all__ = (
|
|
|
74
76
|
"VectorIndexType",
|
|
75
77
|
"VectorIndexConfig",
|
|
76
78
|
"VectorStorageFormat",
|
|
79
|
+
"VectorStorageType",
|
|
80
|
+
"SparseVector",
|
|
77
81
|
)
|
|
@@ -729,8 +729,22 @@ VECTOR Datatype
|
|
|
729
729
|
|
|
730
730
|
Oracle Database 23ai introduced a new VECTOR datatype for artificial intelligence
|
|
731
731
|
and machine learning search operations. The VECTOR datatype is a homogeneous array
|
|
732
|
-
of 8-bit signed integers, 8-bit unsigned integers (binary), 32-bit floating-point
|
|
733
|
-
or 64-bit floating-point numbers.
|
|
732
|
+
of 8-bit signed integers, 8-bit unsigned integers (binary), 32-bit floating-point
|
|
733
|
+
numbers, or 64-bit floating-point numbers.
|
|
734
|
+
|
|
735
|
+
A vector's storage type can be either DENSE or SPARSE. A dense vector contains
|
|
736
|
+
meaningful values in most or all of its dimensions. In contrast, a sparse vector
|
|
737
|
+
has non-zero values in only a few dimensions, with the majority being zero.
|
|
738
|
+
|
|
739
|
+
Sparse vectors are represented by the total number of vector dimensions, an array
|
|
740
|
+
of indices, and an array of values where each value’s location in the vector is
|
|
741
|
+
indicated by the corresponding indices array position. All other vector values are
|
|
742
|
+
treated as zero.
|
|
743
|
+
|
|
744
|
+
The storage formats that can be used with sparse vectors are float32, float64, and
|
|
745
|
+
int8. Note that the binary storage format cannot be used with sparse vectors.
|
|
746
|
+
|
|
747
|
+
Sparse vectors are supported when you are using Oracle Database 23.7 or later.
|
|
734
748
|
|
|
735
749
|
.. seealso::
|
|
736
750
|
|
|
@@ -738,17 +752,26 @@ or 64-bit floating-point numbers.
|
|
|
738
752
|
<https://python-oracledb.readthedocs.io/en/latest/user_guide/vector_data_type.html>`_ - in the documentation
|
|
739
753
|
for the :ref:`oracledb` driver.
|
|
740
754
|
|
|
741
|
-
.. versionadded:: 2.0.41
|
|
755
|
+
.. versionadded:: 2.0.41 - Added VECTOR datatype
|
|
756
|
+
|
|
757
|
+
.. versionadded:: 2.0.43 - Added DENSE/SPARSE support
|
|
742
758
|
|
|
743
759
|
CREATE TABLE support for VECTOR
|
|
744
760
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
745
761
|
|
|
746
|
-
With the :class:`.VECTOR` datatype, you can specify the
|
|
747
|
-
|
|
748
|
-
:class:`.VectorStorageFormat`.
|
|
749
|
-
:class:`.
|
|
762
|
+
With the :class:`.VECTOR` datatype, you can specify the number of dimensions,
|
|
763
|
+
the storage format, and the storage type for the data. Valid values for the
|
|
764
|
+
storage format are enum members of :class:`.VectorStorageFormat`. Valid values
|
|
765
|
+
for the storage type are enum members of :class:`.VectorStorageType`. If
|
|
766
|
+
storage type is not specified, a DENSE vector is created by default.
|
|
767
|
+
|
|
768
|
+
To create a table that includes a :class:`.VECTOR` column::
|
|
750
769
|
|
|
751
|
-
from sqlalchemy.dialects.oracle import
|
|
770
|
+
from sqlalchemy.dialects.oracle import (
|
|
771
|
+
VECTOR,
|
|
772
|
+
VectorStorageFormat,
|
|
773
|
+
VectorStorageType,
|
|
774
|
+
)
|
|
752
775
|
|
|
753
776
|
t = Table(
|
|
754
777
|
"t1",
|
|
@@ -756,7 +779,11 @@ and the storage format. Valid values for storage format are enum values from
|
|
|
756
779
|
Column("id", Integer, primary_key=True),
|
|
757
780
|
Column(
|
|
758
781
|
"embedding",
|
|
759
|
-
VECTOR(
|
|
782
|
+
VECTOR(
|
|
783
|
+
dim=3,
|
|
784
|
+
storage_format=VectorStorageFormat.FLOAT32,
|
|
785
|
+
storage_type=VectorStorageType.SPARSE,
|
|
786
|
+
),
|
|
760
787
|
),
|
|
761
788
|
Column(...),
|
|
762
789
|
...,
|
|
@@ -764,31 +791,40 @@ and the storage format. Valid values for storage format are enum values from
|
|
|
764
791
|
|
|
765
792
|
Vectors can also be defined with an arbitrary number of dimensions and formats.
|
|
766
793
|
This allows you to specify vectors of different dimensions with the various
|
|
767
|
-
storage formats mentioned
|
|
794
|
+
storage formats mentioned below.
|
|
768
795
|
|
|
769
796
|
**Examples**
|
|
770
797
|
|
|
771
|
-
* In this case, the storage format is flexible, allowing any vector type data to be
|
|
772
|
-
such as INT8 or BINARY etc::
|
|
798
|
+
* In this case, the storage format is flexible, allowing any vector type data to be
|
|
799
|
+
inserted, such as INT8 or BINARY etc::
|
|
773
800
|
|
|
774
801
|
vector_col: Mapped[array.array] = mapped_column(VECTOR(dim=3))
|
|
775
802
|
|
|
776
|
-
* The dimension is flexible in this case, meaning that any dimension vector can
|
|
803
|
+
* The dimension is flexible in this case, meaning that any dimension vector can
|
|
804
|
+
be used::
|
|
777
805
|
|
|
778
806
|
vector_col: Mapped[array.array] = mapped_column(
|
|
779
807
|
VECTOR(storage_format=VectorStorageType.INT8)
|
|
780
808
|
)
|
|
781
809
|
|
|
782
|
-
* Both the dimensions and the storage format are flexible::
|
|
810
|
+
* Both the dimensions and the storage format are flexible. It creates a DENSE vector::
|
|
783
811
|
|
|
784
812
|
vector_col: Mapped[array.array] = mapped_column(VECTOR)
|
|
785
813
|
|
|
814
|
+
* To create a SPARSE vector with both dimensions and the storage format as flexible,
|
|
815
|
+
use the :attr:`.VectorStorageType.SPARSE` storage type::
|
|
816
|
+
|
|
817
|
+
vector_col: Mapped[array.array] = mapped_column(
|
|
818
|
+
VECTOR(storage_type=VectorStorageType.SPARSE)
|
|
819
|
+
)
|
|
820
|
+
|
|
786
821
|
Python Datatypes for VECTOR
|
|
787
822
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
788
823
|
|
|
789
824
|
VECTOR data can be inserted using Python list or Python ``array.array()`` objects.
|
|
790
|
-
Python arrays of type FLOAT (32-bit), DOUBLE (64-bit),
|
|
791
|
-
are used as bind values when inserting
|
|
825
|
+
Python arrays of type FLOAT (32-bit), DOUBLE (64-bit), INT (8-bit signed integers),
|
|
826
|
+
or BINARY (8-bit unsigned integers) are used as bind values when inserting
|
|
827
|
+
VECTOR columns::
|
|
792
828
|
|
|
793
829
|
from sqlalchemy import insert, select
|
|
794
830
|
|
|
@@ -798,6 +834,21 @@ are used as bind values when inserting VECTOR columns::
|
|
|
798
834
|
{"id": 1, "embedding": [1, 2, 3]},
|
|
799
835
|
)
|
|
800
836
|
|
|
837
|
+
Data can be inserted into a sparse vector using the :class:`_oracle.SparseVector`
|
|
838
|
+
class, creating an object consisting of the number of dimensions, an array of indices, and a
|
|
839
|
+
corresponding array of values::
|
|
840
|
+
|
|
841
|
+
from sqlalchemy import insert, select
|
|
842
|
+
from sqlalchemy.dialects.oracle import SparseVector
|
|
843
|
+
|
|
844
|
+
sparse_val = SparseVector(10, [1, 2], array.array("d", [23.45, 221.22]))
|
|
845
|
+
|
|
846
|
+
with engine.begin() as conn:
|
|
847
|
+
conn.execute(
|
|
848
|
+
insert(t1),
|
|
849
|
+
{"id": 1, "embedding": sparse_val},
|
|
850
|
+
)
|
|
851
|
+
|
|
801
852
|
VECTOR Indexes
|
|
802
853
|
~~~~~~~~~~~~~~
|
|
803
854
|
|
|
@@ -805,6 +856,8 @@ The VECTOR feature supports an Oracle-specific parameter ``oracle_vector``
|
|
|
805
856
|
on the :class:`.Index` construct, which allows the construction of VECTOR
|
|
806
857
|
indexes.
|
|
807
858
|
|
|
859
|
+
SPARSE vectors cannot be used in the creation of vector indexes.
|
|
860
|
+
|
|
808
861
|
To utilize VECTOR indexing, set the ``oracle_vector`` parameter to True to use
|
|
809
862
|
the default values provided by Oracle. HNSW is the default indexing method::
|
|
810
863
|
|
|
@@ -1157,14 +1210,16 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
|
|
|
1157
1210
|
return "ROWID"
|
|
1158
1211
|
|
|
1159
1212
|
def visit_VECTOR(self, type_, **kw):
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1213
|
+
dim = type_.dim if type_.dim is not None else "*"
|
|
1214
|
+
storage_format = (
|
|
1215
|
+
type_.storage_format.value
|
|
1216
|
+
if type_.storage_format is not None
|
|
1217
|
+
else "*"
|
|
1218
|
+
)
|
|
1219
|
+
storage_type = (
|
|
1220
|
+
type_.storage_type.value if type_.storage_type is not None else "*"
|
|
1221
|
+
)
|
|
1222
|
+
return f"VECTOR({dim},{storage_format},{storage_type})"
|
|
1168
1223
|
|
|
1169
1224
|
|
|
1170
1225
|
class OracleCompiler(compiler.SQLCompiler):
|
|
@@ -1234,6 +1234,9 @@ class OracleDialect_cx_oracle(OracleDialect):
|
|
|
1234
1234
|
with dbapi_connection.cursor() as cursor:
|
|
1235
1235
|
cursor.execute(f"ALTER SESSION SET ISOLATION_LEVEL={level}")
|
|
1236
1236
|
|
|
1237
|
+
def detect_autocommit_setting(self, dbapi_conn) -> bool:
|
|
1238
|
+
return bool(dbapi_conn.autocommit)
|
|
1239
|
+
|
|
1237
1240
|
def _detect_decimal_char(self, connection):
|
|
1238
1241
|
# we have the option to change this setting upon connect,
|
|
1239
1242
|
# or just look at what it is upon connect and convert.
|
|
@@ -13,6 +13,7 @@ import array
|
|
|
13
13
|
from dataclasses import dataclass
|
|
14
14
|
from enum import Enum
|
|
15
15
|
from typing import Optional
|
|
16
|
+
from typing import Union
|
|
16
17
|
|
|
17
18
|
import sqlalchemy.types as types
|
|
18
19
|
from sqlalchemy.types import Float
|
|
@@ -95,6 +96,27 @@ class VectorStorageFormat(Enum):
|
|
|
95
96
|
"""
|
|
96
97
|
|
|
97
98
|
|
|
99
|
+
class VectorStorageType(Enum):
|
|
100
|
+
"""Enum representing the vector type,
|
|
101
|
+
|
|
102
|
+
See :ref:`oracle_vector_datatype` for background.
|
|
103
|
+
|
|
104
|
+
.. versionadded:: 2.0.43
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
SPARSE = "SPARSE"
|
|
109
|
+
"""
|
|
110
|
+
A Sparse vector is a vector which has zero value for
|
|
111
|
+
most of its dimensions.
|
|
112
|
+
"""
|
|
113
|
+
DENSE = "DENSE"
|
|
114
|
+
"""
|
|
115
|
+
A Dense vector is a vector where most, if not all, elements
|
|
116
|
+
hold meaningful values.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
|
|
98
120
|
@dataclass
|
|
99
121
|
class VectorIndexConfig:
|
|
100
122
|
"""Define the configuration for Oracle VECTOR Index.
|
|
@@ -176,6 +198,39 @@ class VectorIndexConfig:
|
|
|
176
198
|
)
|
|
177
199
|
|
|
178
200
|
|
|
201
|
+
class SparseVector:
|
|
202
|
+
"""
|
|
203
|
+
Lightweight SQLAlchemy-side version of SparseVector.
|
|
204
|
+
This mimics oracledb.SparseVector.
|
|
205
|
+
|
|
206
|
+
.. versionadded:: 2.0.43
|
|
207
|
+
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def __init__(
|
|
211
|
+
self,
|
|
212
|
+
num_dimensions: int,
|
|
213
|
+
indices: Union[list, array.array],
|
|
214
|
+
values: Union[list, array.array],
|
|
215
|
+
):
|
|
216
|
+
if not isinstance(indices, array.array) or indices.typecode != "I":
|
|
217
|
+
indices = array.array("I", indices)
|
|
218
|
+
if not isinstance(values, array.array):
|
|
219
|
+
values = array.array("d", values)
|
|
220
|
+
if len(indices) != len(values):
|
|
221
|
+
raise TypeError("indices and values must be of the same length!")
|
|
222
|
+
|
|
223
|
+
self.num_dimensions = num_dimensions
|
|
224
|
+
self.indices = indices
|
|
225
|
+
self.values = values
|
|
226
|
+
|
|
227
|
+
def __str__(self):
|
|
228
|
+
return (
|
|
229
|
+
f"SparseVector(num_dimensions={self.num_dimensions}, "
|
|
230
|
+
f"size={len(self.indices)}, typecode={self.values.typecode})"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
179
234
|
class VECTOR(types.TypeEngine):
|
|
180
235
|
"""Oracle VECTOR datatype.
|
|
181
236
|
|
|
@@ -196,17 +251,22 @@ class VECTOR(types.TypeEngine):
|
|
|
196
251
|
VectorStorageFormat.FLOAT64: "d", # Double
|
|
197
252
|
}
|
|
198
253
|
|
|
199
|
-
def __init__(self, dim=None, storage_format=None):
|
|
254
|
+
def __init__(self, dim=None, storage_format=None, storage_type=None):
|
|
200
255
|
"""Construct a VECTOR.
|
|
201
256
|
|
|
202
257
|
:param dim: integer. The dimension of the VECTOR datatype. This
|
|
203
258
|
should be an integer value.
|
|
204
259
|
|
|
205
260
|
:param storage_format: VectorStorageFormat. The VECTOR storage
|
|
206
|
-
type format. This
|
|
261
|
+
type format. This should be Enum values form
|
|
207
262
|
:class:`.VectorStorageFormat` INT8, BINARY, FLOAT32, or FLOAT64.
|
|
208
263
|
|
|
264
|
+
:param storage_type: VectorStorageType. The Vector storage type. This
|
|
265
|
+
should be Enum values from :class:`.VectorStorageType` SPARSE or
|
|
266
|
+
DENSE.
|
|
267
|
+
|
|
209
268
|
"""
|
|
269
|
+
|
|
210
270
|
if dim is not None and not isinstance(dim, int):
|
|
211
271
|
raise TypeError("dim must be an interger")
|
|
212
272
|
if storage_format is not None and not isinstance(
|
|
@@ -215,12 +275,22 @@ class VECTOR(types.TypeEngine):
|
|
|
215
275
|
raise TypeError(
|
|
216
276
|
"storage_format must be an enum of type VectorStorageFormat"
|
|
217
277
|
)
|
|
278
|
+
if storage_type is not None and not isinstance(
|
|
279
|
+
storage_type, VectorStorageType
|
|
280
|
+
):
|
|
281
|
+
raise TypeError(
|
|
282
|
+
"storage_type must be an enum of type VectorStorageType"
|
|
283
|
+
)
|
|
284
|
+
|
|
218
285
|
self.dim = dim
|
|
219
286
|
self.storage_format = storage_format
|
|
287
|
+
self.storage_type = storage_type
|
|
220
288
|
|
|
221
289
|
def _cached_bind_processor(self, dialect):
|
|
222
290
|
"""
|
|
223
|
-
|
|
291
|
+
Converts a Python-side SparseVector instance into an
|
|
292
|
+
oracledb.SparseVectormor a compatible array format before
|
|
293
|
+
binding it to the database.
|
|
224
294
|
"""
|
|
225
295
|
|
|
226
296
|
def process(value):
|
|
@@ -233,20 +303,48 @@ class VECTOR(types.TypeEngine):
|
|
|
233
303
|
value = array.array(typecode, value)
|
|
234
304
|
return value
|
|
235
305
|
|
|
306
|
+
# Convert SqlAlchemy SparseVector to oracledb SparseVector object
|
|
307
|
+
elif isinstance(value, SparseVector):
|
|
308
|
+
return dialect.dbapi.SparseVector(
|
|
309
|
+
value.num_dimensions,
|
|
310
|
+
value.indices,
|
|
311
|
+
value.values,
|
|
312
|
+
)
|
|
313
|
+
|
|
236
314
|
else:
|
|
237
|
-
raise TypeError(
|
|
315
|
+
raise TypeError(
|
|
316
|
+
"""
|
|
317
|
+
Invalid input for VECTOR: expected a list, an array.array,
|
|
318
|
+
or a SparseVector object.
|
|
319
|
+
"""
|
|
320
|
+
)
|
|
238
321
|
|
|
239
322
|
return process
|
|
240
323
|
|
|
241
324
|
def _cached_result_processor(self, dialect, coltype):
|
|
242
325
|
"""
|
|
243
|
-
|
|
326
|
+
Converts database-returned values into Python-native representations.
|
|
327
|
+
If the value is an oracledb.SparseVector, it is converted into the
|
|
328
|
+
SQLAlchemy-side SparseVector class.
|
|
329
|
+
If the value is a array.array, it is converted to a plain Python list.
|
|
330
|
+
|
|
244
331
|
"""
|
|
245
332
|
|
|
246
333
|
def process(value):
|
|
247
|
-
if
|
|
334
|
+
if value is None:
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
elif isinstance(value, array.array):
|
|
248
338
|
return list(value)
|
|
249
339
|
|
|
340
|
+
# Convert Oracledb SparseVector to SqlAlchemy SparseVector object
|
|
341
|
+
elif isinstance(value, dialect.dbapi.SparseVector):
|
|
342
|
+
return SparseVector(
|
|
343
|
+
num_dimensions=value.num_dimensions,
|
|
344
|
+
indices=value.indices,
|
|
345
|
+
values=value.values,
|
|
346
|
+
)
|
|
347
|
+
|
|
250
348
|
return process
|
|
251
349
|
|
|
252
350
|
def _array_typecode(self, typecode):
|
|
@@ -170,6 +170,9 @@ class _PGDialect_common_psycopg(PGDialect):
|
|
|
170
170
|
def _do_autocommit(self, connection, value):
|
|
171
171
|
connection.autocommit = value
|
|
172
172
|
|
|
173
|
+
def detect_autocommit_setting(self, dbapi_connection):
|
|
174
|
+
return bool(dbapi_connection.autocommit)
|
|
175
|
+
|
|
173
176
|
def do_ping(self, dbapi_connection):
|
|
174
177
|
before_autocommit = dbapi_connection.autocommit
|
|
175
178
|
|
|
@@ -1117,6 +1117,9 @@ class PGDialect_asyncpg(PGDialect):
|
|
|
1117
1117
|
def set_isolation_level(self, dbapi_connection, level):
|
|
1118
1118
|
dbapi_connection.set_isolation_level(self._isolation_lookup[level])
|
|
1119
1119
|
|
|
1120
|
+
def detect_autocommit_setting(self, dbapi_conn) -> bool:
|
|
1121
|
+
return bool(dbapi_conn.autocommit)
|
|
1122
|
+
|
|
1120
1123
|
def set_readonly(self, connection, value):
|
|
1121
1124
|
connection.readonly = value
|
|
1122
1125
|
|
|
@@ -540,6 +540,9 @@ class PGDialect_pg8000(PGDialect):
|
|
|
540
540
|
cursor.execute("COMMIT")
|
|
541
541
|
cursor.close()
|
|
542
542
|
|
|
543
|
+
def detect_autocommit_setting(self, dbapi_conn) -> bool:
|
|
544
|
+
return bool(dbapi_conn.autocommit)
|
|
545
|
+
|
|
543
546
|
def set_readonly(self, connection, value):
|
|
544
547
|
cursor = connection.cursor()
|
|
545
548
|
try:
|
|
@@ -396,10 +396,18 @@ new connections through the usage of events::
|
|
|
396
396
|
|
|
397
397
|
@event.listens_for(Engine, "connect")
|
|
398
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
|
+
|
|
399
404
|
cursor = dbapi_connection.cursor()
|
|
400
405
|
cursor.execute("PRAGMA foreign_keys=ON")
|
|
401
406
|
cursor.close()
|
|
402
407
|
|
|
408
|
+
# restore previous autocommit setting
|
|
409
|
+
dbapi_connection.autocommit = ac
|
|
410
|
+
|
|
403
411
|
.. warning::
|
|
404
412
|
|
|
405
413
|
When SQLite foreign keys are enabled, it is **not possible**
|
|
@@ -502,6 +502,9 @@ class SQLiteDialect_pysqlite(SQLiteDialect):
|
|
|
502
502
|
dbapi_connection.isolation_level = ""
|
|
503
503
|
return super().set_isolation_level(dbapi_connection, level)
|
|
504
504
|
|
|
505
|
+
def detect_autocommit_setting(self, dbapi_connection):
|
|
506
|
+
return dbapi_connection.isolation_level is None
|
|
507
|
+
|
|
505
508
|
def on_connect(self):
|
|
506
509
|
def regexp(a, b):
|
|
507
510
|
if b is None:
|
sqlalchemy/engine/base.py
CHANGED
|
@@ -1112,10 +1112,16 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
|
|
|
1112
1112
|
if self._still_open_and_dbapi_connection_is_valid:
|
|
1113
1113
|
if self._echo:
|
|
1114
1114
|
if self._is_autocommit_isolation():
|
|
1115
|
-
self.
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1115
|
+
if self.dialect.skip_autocommit_rollback:
|
|
1116
|
+
self._log_info(
|
|
1117
|
+
"ROLLBACK will be skipped by "
|
|
1118
|
+
"skip_autocommit_rollback"
|
|
1119
|
+
)
|
|
1120
|
+
else:
|
|
1121
|
+
self._log_info(
|
|
1122
|
+
"ROLLBACK using DBAPI connection.rollback(); "
|
|
1123
|
+
"set skip_autocommit_rollback to prevent fully"
|
|
1124
|
+
)
|
|
1119
1125
|
else:
|
|
1120
1126
|
self._log_info("ROLLBACK")
|
|
1121
1127
|
try:
|
|
@@ -1131,7 +1137,7 @@ class Connection(ConnectionEventsTarget, inspection.Inspectable["Inspector"]):
|
|
|
1131
1137
|
if self._is_autocommit_isolation():
|
|
1132
1138
|
self._log_info(
|
|
1133
1139
|
"COMMIT using DBAPI connection.commit(), "
|
|
1134
|
-
"
|
|
1140
|
+
"has no effect due to autocommit mode"
|
|
1135
1141
|
)
|
|
1136
1142
|
else:
|
|
1137
1143
|
self._log_info("COMMIT")
|
sqlalchemy/engine/create.py
CHANGED
|
@@ -468,6 +468,9 @@ def create_engine(url: Union[str, _url.URL], **kwargs: Any) -> Engine:
|
|
|
468
468
|
|
|
469
469
|
:ref:`pool_reset_on_return`
|
|
470
470
|
|
|
471
|
+
:ref:`dbapi_autocommit_skip_rollback` - a more modern approach
|
|
472
|
+
to using connections with no transactional instructions
|
|
473
|
+
|
|
471
474
|
:param pool_timeout=30: number of seconds to wait before giving
|
|
472
475
|
up on getting a connection from the pool. This is only used
|
|
473
476
|
with :class:`~sqlalchemy.pool.QueuePool`. This can be a float but is
|
|
@@ -524,6 +527,18 @@ def create_engine(url: Union[str, _url.URL], **kwargs: Any) -> Engine:
|
|
|
524
527
|
|
|
525
528
|
.. versionadded:: 1.4
|
|
526
529
|
|
|
530
|
+
:param skip_autocommit_rollback: When True, the dialect will
|
|
531
|
+
unconditionally skip all calls to the DBAPI ``connection.rollback()``
|
|
532
|
+
method if the DBAPI connection is confirmed to be in "autocommit" mode.
|
|
533
|
+
The availability of this feature is dialect specific; if not available,
|
|
534
|
+
a ``NotImplementedError`` is raised by the dialect when rollback occurs.
|
|
535
|
+
|
|
536
|
+
.. seealso::
|
|
537
|
+
|
|
538
|
+
:ref:`dbapi_autocommit_skip_rollback`
|
|
539
|
+
|
|
540
|
+
.. versionadded:: 2.0.43
|
|
541
|
+
|
|
527
542
|
:param use_insertmanyvalues: True by default, use the "insertmanyvalues"
|
|
528
543
|
execution style for INSERT..RETURNING statements by default.
|
|
529
544
|
|
sqlalchemy/engine/default.py
CHANGED
|
@@ -307,6 +307,7 @@ class DefaultDialect(Dialect):
|
|
|
307
307
|
# Linting.NO_LINTING constant
|
|
308
308
|
compiler_linting: Linting = int(compiler.NO_LINTING), # type: ignore
|
|
309
309
|
server_side_cursors: bool = False,
|
|
310
|
+
skip_autocommit_rollback: bool = False,
|
|
310
311
|
**kwargs: Any,
|
|
311
312
|
):
|
|
312
313
|
if server_side_cursors:
|
|
@@ -331,6 +332,8 @@ class DefaultDialect(Dialect):
|
|
|
331
332
|
|
|
332
333
|
self.dbapi = dbapi
|
|
333
334
|
|
|
335
|
+
self.skip_autocommit_rollback = skip_autocommit_rollback
|
|
336
|
+
|
|
334
337
|
if paramstyle is not None:
|
|
335
338
|
self.paramstyle = paramstyle
|
|
336
339
|
elif self.dbapi is not None:
|
|
@@ -701,6 +704,10 @@ class DefaultDialect(Dialect):
|
|
|
701
704
|
pass
|
|
702
705
|
|
|
703
706
|
def do_rollback(self, dbapi_connection):
|
|
707
|
+
if self.skip_autocommit_rollback and self.detect_autocommit_setting(
|
|
708
|
+
dbapi_connection
|
|
709
|
+
):
|
|
710
|
+
return
|
|
704
711
|
dbapi_connection.rollback()
|
|
705
712
|
|
|
706
713
|
def do_commit(self, dbapi_connection):
|
sqlalchemy/engine/interfaces.py
CHANGED
|
@@ -779,6 +779,14 @@ class Dialect(EventTarget):
|
|
|
779
779
|
default_isolation_level: Optional[IsolationLevel]
|
|
780
780
|
"""the isolation that is implicitly present on new connections"""
|
|
781
781
|
|
|
782
|
+
skip_autocommit_rollback: bool
|
|
783
|
+
"""Whether or not the :paramref:`.create_engine.skip_autocommit_rollback`
|
|
784
|
+
parameter was set.
|
|
785
|
+
|
|
786
|
+
.. versionadded:: 2.0.43
|
|
787
|
+
|
|
788
|
+
"""
|
|
789
|
+
|
|
782
790
|
# create_engine() -> isolation_level currently goes here
|
|
783
791
|
_on_connect_isolation_level: Optional[IsolationLevel]
|
|
784
792
|
|
|
@@ -2481,6 +2489,30 @@ class Dialect(EventTarget):
|
|
|
2481
2489
|
|
|
2482
2490
|
raise NotImplementedError()
|
|
2483
2491
|
|
|
2492
|
+
def detect_autocommit_setting(self, dbapi_conn: DBAPIConnection) -> bool:
|
|
2493
|
+
"""Detect the current autocommit setting for a DBAPI connection.
|
|
2494
|
+
|
|
2495
|
+
:param dbapi_connection: a DBAPI connection object
|
|
2496
|
+
:return: True if autocommit is enabled, False if disabled
|
|
2497
|
+
:rtype: bool
|
|
2498
|
+
|
|
2499
|
+
This method inspects the given DBAPI connection to determine
|
|
2500
|
+
whether autocommit mode is currently enabled. The specific
|
|
2501
|
+
mechanism for detecting autocommit varies by database dialect
|
|
2502
|
+
and DBAPI driver, however it should be done **without** network
|
|
2503
|
+
round trips.
|
|
2504
|
+
|
|
2505
|
+
.. note::
|
|
2506
|
+
|
|
2507
|
+
Not all dialects support autocommit detection. Dialects
|
|
2508
|
+
that do not support this feature will raise
|
|
2509
|
+
:exc:`NotImplementedError`.
|
|
2510
|
+
|
|
2511
|
+
"""
|
|
2512
|
+
raise NotImplementedError(
|
|
2513
|
+
"This dialect cannot detect autocommit on a DBAPI connection"
|
|
2514
|
+
)
|
|
2515
|
+
|
|
2484
2516
|
def get_default_isolation_level(
|
|
2485
2517
|
self, dbapi_conn: DBAPIConnection
|
|
2486
2518
|
) -> IsolationLevel:
|
sqlalchemy/engine/result.py
CHANGED
|
@@ -2013,7 +2013,7 @@ class MappingResult(_WithKeys, FilterResult[RowMapping]):
|
|
|
2013
2013
|
return self
|
|
2014
2014
|
|
|
2015
2015
|
def columns(self, *col_expressions: _KeyIndexType) -> Self:
|
|
2016
|
-
|
|
2016
|
+
"""Establish the columns that should be returned in each row."""
|
|
2017
2017
|
return self._column_slices(col_expressions)
|
|
2018
2018
|
|
|
2019
2019
|
def partitions(
|
|
@@ -393,7 +393,9 @@ class CompositeProperty(
|
|
|
393
393
|
self.composite_class = argument
|
|
394
394
|
|
|
395
395
|
if is_dataclass(self.composite_class):
|
|
396
|
-
self._setup_for_dataclass(
|
|
396
|
+
self._setup_for_dataclass(
|
|
397
|
+
decl_scan, registry, cls, originating_module, key
|
|
398
|
+
)
|
|
397
399
|
else:
|
|
398
400
|
for attr in self.attrs:
|
|
399
401
|
if (
|
|
@@ -437,6 +439,7 @@ class CompositeProperty(
|
|
|
437
439
|
@util.preload_module("sqlalchemy.orm.decl_base")
|
|
438
440
|
def _setup_for_dataclass(
|
|
439
441
|
self,
|
|
442
|
+
decl_scan: _ClassScanMapperConfig,
|
|
440
443
|
registry: _RegistryType,
|
|
441
444
|
cls: Type[Any],
|
|
442
445
|
originating_module: Optional[str],
|
|
@@ -464,6 +467,7 @@ class CompositeProperty(
|
|
|
464
467
|
|
|
465
468
|
if isinstance(attr, MappedColumn):
|
|
466
469
|
attr.declarative_scan_for_composite(
|
|
470
|
+
decl_scan,
|
|
467
471
|
registry,
|
|
468
472
|
cls,
|
|
469
473
|
originating_module,
|
sqlalchemy/orm/loading.py
CHANGED
|
@@ -39,6 +39,7 @@ from .base import PassiveFlag
|
|
|
39
39
|
from .context import FromStatement
|
|
40
40
|
from .context import ORMCompileState
|
|
41
41
|
from .context import QueryContext
|
|
42
|
+
from .strategies import SelectInLoader
|
|
42
43
|
from .util import _none_set
|
|
43
44
|
from .util import state_str
|
|
44
45
|
from .. import exc as sa_exc
|
|
@@ -1309,15 +1310,18 @@ def _load_subclass_via_in(
|
|
|
1309
1310
|
if context.populate_existing:
|
|
1310
1311
|
q2 = q2.execution_options(populate_existing=True)
|
|
1311
1312
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1313
|
+
while states:
|
|
1314
|
+
chunk = states[0 : SelectInLoader._chunksize]
|
|
1315
|
+
states = states[SelectInLoader._chunksize :]
|
|
1316
|
+
context.session.execute(
|
|
1317
|
+
q2,
|
|
1318
|
+
dict(
|
|
1319
|
+
primary_keys=[
|
|
1320
|
+
state.key[1][0] if zero_idx else state.key[1]
|
|
1321
|
+
for state, load_attrs in chunk
|
|
1322
|
+
]
|
|
1323
|
+
),
|
|
1324
|
+
).unique().scalars().all()
|
|
1321
1325
|
|
|
1322
1326
|
return do_load
|
|
1323
1327
|
|
sqlalchemy/orm/persistence.py
CHANGED
|
@@ -1374,7 +1374,13 @@ def _emit_post_update_statements(
|
|
|
1374
1374
|
)
|
|
1375
1375
|
|
|
1376
1376
|
rows += c.rowcount
|
|
1377
|
-
for
|
|
1377
|
+
for i, (
|
|
1378
|
+
state,
|
|
1379
|
+
state_dict,
|
|
1380
|
+
mapper_rec,
|
|
1381
|
+
connection,
|
|
1382
|
+
params,
|
|
1383
|
+
) in enumerate(records):
|
|
1378
1384
|
_postfetch_post_update(
|
|
1379
1385
|
mapper_rec,
|
|
1380
1386
|
uowtransaction,
|
|
@@ -1382,7 +1388,7 @@ def _emit_post_update_statements(
|
|
|
1382
1388
|
state,
|
|
1383
1389
|
state_dict,
|
|
1384
1390
|
c,
|
|
1385
|
-
c.context.compiled_parameters[
|
|
1391
|
+
c.context.compiled_parameters[i],
|
|
1386
1392
|
)
|
|
1387
1393
|
|
|
1388
1394
|
if check_rowcount:
|
sqlalchemy/orm/properties.py
CHANGED
|
@@ -663,20 +663,12 @@ class MappedColumn(
|
|
|
663
663
|
# Column will be merged into it in _init_column_for_annotation().
|
|
664
664
|
return MappedColumn()
|
|
665
665
|
|
|
666
|
-
def
|
|
666
|
+
def _adjust_for_existing_column(
|
|
667
667
|
self,
|
|
668
668
|
decl_scan: _ClassScanMapperConfig,
|
|
669
|
-
registry: _RegistryType,
|
|
670
|
-
cls: Type[Any],
|
|
671
|
-
originating_module: Optional[str],
|
|
672
669
|
key: str,
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
extracted_mapped_annotation: Optional[_AnnotationScanType],
|
|
676
|
-
is_dataclass_field: bool,
|
|
677
|
-
) -> None:
|
|
678
|
-
column = self.column
|
|
679
|
-
|
|
670
|
+
given_column: Column[_T],
|
|
671
|
+
) -> Column[_T]:
|
|
680
672
|
if (
|
|
681
673
|
self._use_existing_column
|
|
682
674
|
and decl_scan.inherits
|
|
@@ -688,10 +680,31 @@ class MappedColumn(
|
|
|
688
680
|
)
|
|
689
681
|
supercls_mapper = class_mapper(decl_scan.inherits, False)
|
|
690
682
|
|
|
691
|
-
colname =
|
|
692
|
-
|
|
693
|
-
colname, column
|
|
683
|
+
colname = (
|
|
684
|
+
given_column.name if given_column.name is not None else key
|
|
694
685
|
)
|
|
686
|
+
given_column = supercls_mapper.local_table.c.get( # type: ignore[assignment] # noqa: E501
|
|
687
|
+
colname, given_column
|
|
688
|
+
)
|
|
689
|
+
return given_column
|
|
690
|
+
|
|
691
|
+
def declarative_scan(
|
|
692
|
+
self,
|
|
693
|
+
decl_scan: _ClassScanMapperConfig,
|
|
694
|
+
registry: _RegistryType,
|
|
695
|
+
cls: Type[Any],
|
|
696
|
+
originating_module: Optional[str],
|
|
697
|
+
key: str,
|
|
698
|
+
mapped_container: Optional[Type[Mapped[Any]]],
|
|
699
|
+
annotation: Optional[_AnnotationScanType],
|
|
700
|
+
extracted_mapped_annotation: Optional[_AnnotationScanType],
|
|
701
|
+
is_dataclass_field: bool,
|
|
702
|
+
) -> None:
|
|
703
|
+
column = self.column
|
|
704
|
+
|
|
705
|
+
column = self.column = self._adjust_for_existing_column(
|
|
706
|
+
decl_scan, key, self.column
|
|
707
|
+
)
|
|
695
708
|
|
|
696
709
|
if column.key is None:
|
|
697
710
|
column.key = key
|
|
@@ -708,6 +721,8 @@ class MappedColumn(
|
|
|
708
721
|
|
|
709
722
|
self._init_column_for_annotation(
|
|
710
723
|
cls,
|
|
724
|
+
decl_scan,
|
|
725
|
+
key,
|
|
711
726
|
registry,
|
|
712
727
|
extracted_mapped_annotation,
|
|
713
728
|
originating_module,
|
|
@@ -716,6 +731,7 @@ class MappedColumn(
|
|
|
716
731
|
@util.preload_module("sqlalchemy.orm.decl_base")
|
|
717
732
|
def declarative_scan_for_composite(
|
|
718
733
|
self,
|
|
734
|
+
decl_scan: _ClassScanMapperConfig,
|
|
719
735
|
registry: _RegistryType,
|
|
720
736
|
cls: Type[Any],
|
|
721
737
|
originating_module: Optional[str],
|
|
@@ -726,12 +742,14 @@ class MappedColumn(
|
|
|
726
742
|
decl_base = util.preloaded.orm_decl_base
|
|
727
743
|
decl_base._undefer_column_name(param_name, self.column)
|
|
728
744
|
self._init_column_for_annotation(
|
|
729
|
-
cls, registry, param_annotation, originating_module
|
|
745
|
+
cls, decl_scan, key, registry, param_annotation, originating_module
|
|
730
746
|
)
|
|
731
747
|
|
|
732
748
|
def _init_column_for_annotation(
|
|
733
749
|
self,
|
|
734
750
|
cls: Type[Any],
|
|
751
|
+
decl_scan: _ClassScanMapperConfig,
|
|
752
|
+
key: str,
|
|
735
753
|
registry: _RegistryType,
|
|
736
754
|
argument: _AnnotationScanType,
|
|
737
755
|
originating_module: Optional[str],
|
|
@@ -778,6 +796,11 @@ class MappedColumn(
|
|
|
778
796
|
use_args_from = None
|
|
779
797
|
|
|
780
798
|
if use_args_from is not None:
|
|
799
|
+
|
|
800
|
+
self.column = use_args_from._adjust_for_existing_column(
|
|
801
|
+
decl_scan, key, self.column
|
|
802
|
+
)
|
|
803
|
+
|
|
781
804
|
if (
|
|
782
805
|
not self._has_insert_default
|
|
783
806
|
and use_args_from.column.default is not None
|
sqlalchemy/sql/functions.py
CHANGED
|
@@ -745,9 +745,7 @@ class FunctionElement(Executable, ColumnElement[_T], FromClause, Generative):
|
|
|
745
745
|
# expressions against getitem. This may need to be made
|
|
746
746
|
# more portable if in the future we support other DBs
|
|
747
747
|
# besides postgresql.
|
|
748
|
-
if against
|
|
749
|
-
self.type, sqltypes.ARRAY
|
|
750
|
-
):
|
|
748
|
+
if against in (operators.getitem, operators.json_getitem_op):
|
|
751
749
|
return Grouping(self)
|
|
752
750
|
else:
|
|
753
751
|
return super().self_group(against=against)
|
|
@@ -1033,6 +1033,13 @@ class SuiteRequirements(Requirements):
|
|
|
1033
1033
|
"""target dialect supports 'AUTOCOMMIT' as an isolation_level"""
|
|
1034
1034
|
return exclusions.closed()
|
|
1035
1035
|
|
|
1036
|
+
@property
|
|
1037
|
+
def skip_autocommit_rollback(self):
|
|
1038
|
+
"""target dialect supports the detect_autocommit_setting() method and
|
|
1039
|
+
uses the default implementation of do_rollback()"""
|
|
1040
|
+
|
|
1041
|
+
return exclusions.closed()
|
|
1042
|
+
|
|
1036
1043
|
@property
|
|
1037
1044
|
def isolation_level(self):
|
|
1038
1045
|
"""target dialect supports general isolation level settings.
|
|
@@ -17,6 +17,7 @@ from .. import eq_
|
|
|
17
17
|
from .. import fixtures
|
|
18
18
|
from .. import is_not_none
|
|
19
19
|
from .. import is_true
|
|
20
|
+
from .. import mock
|
|
20
21
|
from .. import ne_
|
|
21
22
|
from .. import provide_metadata
|
|
22
23
|
from ..assertions import expect_raises
|
|
@@ -293,7 +294,11 @@ class AutocommitIsolationTest(fixtures.TablesTest):
|
|
|
293
294
|
test_needs_acid=True,
|
|
294
295
|
)
|
|
295
296
|
|
|
296
|
-
def _test_conn_autocommits(self, conn, autocommit):
|
|
297
|
+
def _test_conn_autocommits(self, conn, autocommit, ensure_table=False):
|
|
298
|
+
if ensure_table:
|
|
299
|
+
self.tables.some_table.create(conn, checkfirst=True)
|
|
300
|
+
conn.commit()
|
|
301
|
+
|
|
297
302
|
trans = conn.begin()
|
|
298
303
|
conn.execute(
|
|
299
304
|
self.tables.some_table.insert(), {"id": 1, "data": "some data"}
|
|
@@ -336,6 +341,37 @@ class AutocommitIsolationTest(fixtures.TablesTest):
|
|
|
336
341
|
)
|
|
337
342
|
self._test_conn_autocommits(conn, False)
|
|
338
343
|
|
|
344
|
+
@testing.requires.skip_autocommit_rollback
|
|
345
|
+
@testing.variation("autocommit_setting", ["false", "engine", "option"])
|
|
346
|
+
@testing.variation("block_rollback", [True, False])
|
|
347
|
+
def test_autocommit_block(
|
|
348
|
+
self, testing_engine, autocommit_setting, block_rollback
|
|
349
|
+
):
|
|
350
|
+
kw = {}
|
|
351
|
+
if bool(block_rollback):
|
|
352
|
+
kw["skip_autocommit_rollback"] = True
|
|
353
|
+
if autocommit_setting.engine:
|
|
354
|
+
kw["isolation_level"] = "AUTOCOMMIT"
|
|
355
|
+
|
|
356
|
+
engine = testing_engine(options=kw)
|
|
357
|
+
|
|
358
|
+
conn = engine.connect()
|
|
359
|
+
if autocommit_setting.option:
|
|
360
|
+
conn.execution_options(isolation_level="AUTOCOMMIT")
|
|
361
|
+
self._test_conn_autocommits(
|
|
362
|
+
conn,
|
|
363
|
+
autocommit_setting.engine or autocommit_setting.option,
|
|
364
|
+
ensure_table=True,
|
|
365
|
+
)
|
|
366
|
+
with mock.patch.object(
|
|
367
|
+
conn.connection, "rollback", wraps=conn.connection.rollback
|
|
368
|
+
) as check_rollback:
|
|
369
|
+
conn.close()
|
|
370
|
+
if autocommit_setting.false or not block_rollback:
|
|
371
|
+
eq_(check_rollback.mock_calls, [mock.call()])
|
|
372
|
+
else:
|
|
373
|
+
eq_(check_rollback.mock_calls, [])
|
|
374
|
+
|
|
339
375
|
@testing.requires.independent_readonly_connections
|
|
340
376
|
@testing.variation("use_dialect_setting", [True, False])
|
|
341
377
|
def test_dialect_autocommit_is_restored(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sqlalchemy/__init__.py,sha256=
|
|
1
|
+
sqlalchemy/__init__.py,sha256=Oi26seKKS4YLZt2VPHQvkIIoTKAcEWD4BCjHQZRG8BE,12659
|
|
2
2
|
sqlalchemy/events.py,sha256=VBRvtckn9JS3tfUfi6UstqUrvQ15J2xamcDByFysIrI,525
|
|
3
3
|
sqlalchemy/exc.py,sha256=AjFBCrOl_V4vQdGegn72Y951RSRMPL6T5qjxnFTGFbM,23978
|
|
4
4
|
sqlalchemy/inspection.py,sha256=qKEKG37N1OjxpQeVzob1q9VwWjBbjI1x0movJG7fYJ4,5063
|
|
@@ -9,7 +9,7 @@ sqlalchemy/types.py,sha256=Iq_rKisaj_zhHtzD2R2cxvg3jkug5frikbkcKG0S4Lg,3166
|
|
|
9
9
|
sqlalchemy/connectors/__init__.py,sha256=YeSHsOB0YhdM6jZUvHFQFwKqNXO02MlklmGW0yCywjI,476
|
|
10
10
|
sqlalchemy/connectors/aioodbc.py,sha256=-OKbnvR-kLCKHyrOIBkAZwTASAbQZ5qmrozm0dwbtNE,5577
|
|
11
11
|
sqlalchemy/connectors/asyncio.py,sha256=OPhwvKQo7l3CUSY7YsL3W8oBqc_zQIAytIvqLjZLwTA,10122
|
|
12
|
-
sqlalchemy/connectors/pyodbc.py,sha256=
|
|
12
|
+
sqlalchemy/connectors/pyodbc.py,sha256=ZGWBmYYYVgqUHjex3d_lYHZyAhQJGowp9cWGYnj1200,8618
|
|
13
13
|
sqlalchemy/cyextension/__init__.py,sha256=4npVIjitKfUs0NQ6f3UdQBDq4ipJ0_ZNB2mpKqtc5ik,244
|
|
14
14
|
sqlalchemy/cyextension/collections.pyx,sha256=L7DZ3DGKpgw2MT2ZZRRxCnrcyE5pU1NAFowWgAzQPEc,12571
|
|
15
15
|
sqlalchemy/cyextension/immutabledict.pxd,sha256=3x3-rXG5eRQ7bBnktZ-OJ9-6ft8zToPmTDOd92iXpB0,291
|
|
@@ -29,8 +29,8 @@ sqlalchemy/dialects/mssql/provision.py,sha256=P1tqxZ4f6Oeqn2gNi7dXl82LRLCg1-OB4e
|
|
|
29
29
|
sqlalchemy/dialects/mssql/pymssql.py,sha256=C7yAs3Pw81W1KTVNc6_0sHQuYlJ5iH82vKByY4TkB1g,4097
|
|
30
30
|
sqlalchemy/dialects/mssql/pyodbc.py,sha256=CnO7KDWxbxb7AoZhp_PMDBvVSMuzwq1h4Cav2IWFWDo,27173
|
|
31
31
|
sqlalchemy/dialects/mysql/__init__.py,sha256=ropOMUWrAcL-Q7h-9jQ_tb3ISAFIsNRQ8YVXvn0URl0,2206
|
|
32
|
-
sqlalchemy/dialects/mysql/aiomysql.py,sha256=
|
|
33
|
-
sqlalchemy/dialects/mysql/asyncmy.py,sha256=
|
|
32
|
+
sqlalchemy/dialects/mysql/aiomysql.py,sha256=XpHS7KvZF_XQFlghvqyZfPuLD890M7GTgMLCaeXA67E,7728
|
|
33
|
+
sqlalchemy/dialects/mysql/asyncmy.py,sha256=kuX02tRZ-0kKbwRgs3dL5T-mRyc5oBSFoIzQDgaHgYk,7093
|
|
34
34
|
sqlalchemy/dialects/mysql/base.py,sha256=V2CE2XB6eiFG3doNdzH3NZPhgXgt3OL7QN8F3dg_9Pg,137763
|
|
35
35
|
sqlalchemy/dialects/mysql/cymysql.py,sha256=ihH4kZ273nvf0R0p8keD71ZIaTXRHyZePXMlobwgbpI,3215
|
|
36
36
|
sqlalchemy/dialects/mysql/dml.py,sha256=VjnTobe_SBNF2RN6tvqa5LOn-9x4teVUyzUedZkOmdc,7768
|
|
@@ -38,27 +38,27 @@ sqlalchemy/dialects/mysql/enumerated.py,sha256=si2hGv5jMNGS78n_JDgswIhbBZuTqjwbx
|
|
|
38
38
|
sqlalchemy/dialects/mysql/expression.py,sha256=C8LhU-CM6agqKCS1tl1_ChSqwZbqt3zP_dSGBqgBgLg,4241
|
|
39
39
|
sqlalchemy/dialects/mysql/json.py,sha256=ckYT_lihvqr28iHJTUUwvPPUIoYVLL_wUXWFDTCna_M,2806
|
|
40
40
|
sqlalchemy/dialects/mysql/mariadb.py,sha256=yaiZnnbjfrBqHm1ykaRSFYKrrYUqu-GBYvt97EGYSzs,1886
|
|
41
|
-
sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=
|
|
42
|
-
sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=
|
|
43
|
-
sqlalchemy/dialects/mysql/mysqldb.py,sha256=
|
|
41
|
+
sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=lJuS3euMlVBbJDJ10ntqe3TnrjzneLEUlE8sLZl6Qoc,10385
|
|
42
|
+
sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=aaAiF32rQVoLNVIdgGKHMsnMei--0ig3OqmhWq45MrA,10097
|
|
43
|
+
sqlalchemy/dialects/mysql/mysqldb.py,sha256=8wIxcxQxT-X6nywLJkjg9_JdIKGYOhlrtVL8lP_WFcM,9943
|
|
44
44
|
sqlalchemy/dialects/mysql/provision.py,sha256=MaQ9eeHnRL4EXAebIInwarCIiDbYcz_sMCss3wyV12Q,3717
|
|
45
45
|
sqlalchemy/dialects/mysql/pymysql.py,sha256=Qlc9XToIqAfHz0c_ODs97uk1TlV1ZrEl_TidTjoeByU,4886
|
|
46
46
|
sqlalchemy/dialects/mysql/pyodbc.py,sha256=v-Zo4M7blxdff--KJiIantCwbPO6H-GBkNCTN4nBgU4,5111
|
|
47
47
|
sqlalchemy/dialects/mysql/reflection.py,sha256=CBxBiv1mCLLNHz-I8hgJKACTF3K0eYEpWd0ndCBCq5I,24690
|
|
48
48
|
sqlalchemy/dialects/mysql/reserved_words.py,sha256=iG6zb78sn-RdqWQRk2F_Tuufk5tUodkcoHbxTdgZYkw,9236
|
|
49
49
|
sqlalchemy/dialects/mysql/types.py,sha256=lAkkNRVPBHP8H7AQQ7NykfJ8YxgdUDAHkfd7qD-Lwvo,26459
|
|
50
|
-
sqlalchemy/dialects/oracle/__init__.py,sha256=
|
|
51
|
-
sqlalchemy/dialects/oracle/base.py,sha256=
|
|
52
|
-
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=
|
|
50
|
+
sqlalchemy/dialects/oracle/__init__.py,sha256=5qrJcFTF3vgB9B4PkwBJj3iXE7P57LdaHNkxMa1NXug,1898
|
|
51
|
+
sqlalchemy/dialects/oracle/base.py,sha256=zEl885-lRs07FGdWFuSzBfa1FqrUPT7l2wpcBr9joIs,139156
|
|
52
|
+
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=mYrXD0nJzuTY1h878b50fNXIUBgjc9Q1LJjjY1VHx3w,56717
|
|
53
53
|
sqlalchemy/dialects/oracle/dictionary.py,sha256=J7tGVE0KyUPZKpPLOary3HdDq1DWd29arF5udLgv8_o,19519
|
|
54
54
|
sqlalchemy/dialects/oracle/oracledb.py,sha256=veqto1AUIbSxRmpUQin0ysMV8Y6sWAkzXt7W8IIl118,33771
|
|
55
55
|
sqlalchemy/dialects/oracle/provision.py,sha256=ga1gNQZlXZKk7DYuYegllUejJxZXRKDGa7dbi_S_poc,8313
|
|
56
56
|
sqlalchemy/dialects/oracle/types.py,sha256=axN6Yidx9tGRIUAbDpBrhMWXE-C8jSllFpTghpGOOzU,9058
|
|
57
|
-
sqlalchemy/dialects/oracle/vector.py,sha256=
|
|
57
|
+
sqlalchemy/dialects/oracle/vector.py,sha256=YtN7E5TbDIQR2FCICaSeeaOnvzHP_O0mXNq1gk02S4Q,10874
|
|
58
58
|
sqlalchemy/dialects/postgresql/__init__.py,sha256=kD8W-SV5e2CesvWg2MQAtncXuZFwGPfR_UODvmRXE08,3892
|
|
59
|
-
sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=
|
|
59
|
+
sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=h4JmkHWxy_Nspn6Bi9YKpa9l0OkwInwQzYKue-fJnVA,5783
|
|
60
60
|
sqlalchemy/dialects/postgresql/array.py,sha256=FyyJ1f3RSAhHtgxKydfMkUAGEh-LJyLOZ31jiAdDo74,16956
|
|
61
|
-
sqlalchemy/dialects/postgresql/asyncpg.py,sha256=
|
|
61
|
+
sqlalchemy/dialects/postgresql/asyncpg.py,sha256=QPvyV6YYZ9--ULoMYC5pl7axct79H8DbYrKAUQASqzg,41548
|
|
62
62
|
sqlalchemy/dialects/postgresql/base.py,sha256=RDuehOZL3hLPhq4_7G-91BgAM9LeToHiiIU-RjFGVmU,186421
|
|
63
63
|
sqlalchemy/dialects/postgresql/dml.py,sha256=2SmyMeYveAgm7OnT_CJvwad2nh8BP37yT6gFs8dBYN8,12126
|
|
64
64
|
sqlalchemy/dialects/postgresql/ext.py,sha256=voxpAz-zoCOO-fjpCzrw7UASzNIvdz2u4kFSuGcshlI,17347
|
|
@@ -66,7 +66,7 @@ sqlalchemy/dialects/postgresql/hstore.py,sha256=wR4gmvfQWPssHwYTXEsPJTb4LkBS6x4e
|
|
|
66
66
|
sqlalchemy/dialects/postgresql/json.py,sha256=YO6yuDnUKh-mHNtc7DavFMpYNUrJ_dNb24gw333uH0M,12842
|
|
67
67
|
sqlalchemy/dialects/postgresql/named_types.py,sha256=D1WFTcxE-PKYRaB75gWvnAvpgGJRTcFkW9nSGpC4WCo,17812
|
|
68
68
|
sqlalchemy/dialects/postgresql/operators.py,sha256=ay3ckNsWtqDjxDseTdKMGGqYVzST6lmfhbbYHG_bxCw,2808
|
|
69
|
-
sqlalchemy/dialects/postgresql/pg8000.py,sha256=
|
|
69
|
+
sqlalchemy/dialects/postgresql/pg8000.py,sha256=r6Lg5tgwuf4FE_RA_kHcfHPW5GXUdNWWr3E846Z4aI0,18743
|
|
70
70
|
sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=wnzFm9S0JFag1TBdySDJH3VOFSkJWmwAjVcIAQ25jHg,9999
|
|
71
71
|
sqlalchemy/dialects/postgresql/provision.py,sha256=7pg9-nOnaK5XBzqByXNPuvi3rxtnRa3dJxdSPVq4eeA,5770
|
|
72
72
|
sqlalchemy/dialects/postgresql/psycopg.py,sha256=k7zXsJj35aOXCrhsbMxwTQX5JWegrqirFJ1Hgbq-GjQ,23326
|
|
@@ -76,27 +76,27 @@ sqlalchemy/dialects/postgresql/ranges.py,sha256=rsvhfZ63OVtHHeBDXb_6hULg0HkVx18h
|
|
|
76
76
|
sqlalchemy/dialects/postgresql/types.py,sha256=oKhDsFiITKbZcCP66L3dhif54pmsFvVfv-MZQWA3sYo,7629
|
|
77
77
|
sqlalchemy/dialects/sqlite/__init__.py,sha256=6Xcz3nPsl8lqCcZ4-VzPRmkMrkKgAp2buKsClZelU7c,1182
|
|
78
78
|
sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=eZW4NFpLS6z02keIHeJLI5tFUkzhn0MpS8r2kkl0G0I,14619
|
|
79
|
-
sqlalchemy/dialects/sqlite/base.py,sha256=
|
|
79
|
+
sqlalchemy/dialects/sqlite/base.py,sha256=rRYahtQDySw-4v6ljEomUdvjigGTNXqaqPuiQ5eOpa4,102859
|
|
80
80
|
sqlalchemy/dialects/sqlite/dml.py,sha256=4N8qh06RuMphLoQgWw7wv5nXIrka57jIFvK2x9xTZqg,9138
|
|
81
81
|
sqlalchemy/dialects/sqlite/json.py,sha256=A62xPyLRZxl2hvgTMM92jd_7jlw9UE_4Y6Udqt-8g04,2777
|
|
82
82
|
sqlalchemy/dialects/sqlite/provision.py,sha256=VhqDjDALqxKQY_3Z3hjzkmPQJ-vtk2Dkk1A4qLTs-G8,5596
|
|
83
83
|
sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=di8rYryfL0KAn3pRGepmunHyIRGy-4Hhr-2q_ehPzss,5371
|
|
84
|
-
sqlalchemy/dialects/sqlite/pysqlite.py,sha256=
|
|
84
|
+
sqlalchemy/dialects/sqlite/pysqlite.py,sha256=42jPDi1nZ_9YVKKWaKnkurL8NOFUX_8Rbn7baqRw0J8,25999
|
|
85
85
|
sqlalchemy/engine/__init__.py,sha256=EF4haWCPu95WtWx1GzcHRJ_bBmtJMznno3I2TQ-ZIHE,2818
|
|
86
86
|
sqlalchemy/engine/_py_processors.py,sha256=7QxgkVOd5h1Qd22qFh-pPZdM7RBRzNjj8lWAMWrilcI,3744
|
|
87
87
|
sqlalchemy/engine/_py_row.py,sha256=yNdrZe36yw6mO7x0OEbG0dGojH7CQkNReIwn9LMUPUs,3787
|
|
88
88
|
sqlalchemy/engine/_py_util.py,sha256=Nvd4pVdXRs89khRevK-Ux4Y9p2f2vnALboNrSwhqS1U,2465
|
|
89
|
-
sqlalchemy/engine/base.py,sha256=
|
|
89
|
+
sqlalchemy/engine/base.py,sha256=aNp2tGNBWlBz2pHiOveJ3PeaJRDJlLknekUQ50MJDjU,123090
|
|
90
90
|
sqlalchemy/engine/characteristics.py,sha256=PepmGApo1sL01dS1qtSbmHplu9ZCdtuSegiGI7L7NZY,4765
|
|
91
|
-
sqlalchemy/engine/create.py,sha256=
|
|
91
|
+
sqlalchemy/engine/create.py,sha256=uIAiU-ANj7fk_6A3dbJw_SEU8Qfd0_YF8yEHGxD0r1g,33847
|
|
92
92
|
sqlalchemy/engine/cursor.py,sha256=63KLS-IKKAYh2uADJytpT1i9-qpG9E0iVBIcKTtKkwI,76567
|
|
93
|
-
sqlalchemy/engine/default.py,sha256
|
|
93
|
+
sqlalchemy/engine/default.py,sha256=PpySUqbAliGjw80ZxhDdZwyiFEMCpNPcC1XmyJynyEE,85721
|
|
94
94
|
sqlalchemy/engine/events.py,sha256=4_e6Ip32ar2Eb27R4ipamiKC-7Tpg4lVz3txabhT5Rc,37400
|
|
95
|
-
sqlalchemy/engine/interfaces.py,sha256=
|
|
95
|
+
sqlalchemy/engine/interfaces.py,sha256=fNGMov1byIOkPxh7dJervp-UUNyHHm3jpIB0HrCMucc,115119
|
|
96
96
|
sqlalchemy/engine/mock.py,sha256=L07bSIkgEbIkih-pYvFWh7k7adHVp5tBFBekKlD7GHs,4156
|
|
97
97
|
sqlalchemy/engine/processors.py,sha256=XK32bULBkuVVRa703u4-SrTCDi_a18Dxq1M09QFBEPw,2379
|
|
98
98
|
sqlalchemy/engine/reflection.py,sha256=QNOAXvKtdzVddpbkMOyM380y3olKdJKQkmF0Bfwia-Q,75565
|
|
99
|
-
sqlalchemy/engine/result.py,sha256=
|
|
99
|
+
sqlalchemy/engine/result.py,sha256=46J3rP0ZwDwsqU-4CAaEHXTpx8OqCEP9Dy4LQwtHUEg,77805
|
|
100
100
|
sqlalchemy/engine/row.py,sha256=BPtAwsceiRxB9ANpDNM24uQ1M_Zs0xFkSXoKR_I8xyY,12031
|
|
101
101
|
sqlalchemy/engine/strategies.py,sha256=3DixBdeTa824XjuID2o7UxIyg7GyNwdBI8hOOT0SQnc,439
|
|
102
102
|
sqlalchemy/engine/url.py,sha256=GJfZo0KtbMtkOIHBPI_KcKASsyrI5UYkX-UoN62FQxc,31067
|
|
@@ -149,7 +149,7 @@ sqlalchemy/orm/context.py,sha256=9OOJxvXJ_01Sd5-wny-WqVGtak4IA78TyLG_zMOHYmA,115
|
|
|
149
149
|
sqlalchemy/orm/decl_api.py,sha256=ViRNRYA1jXcxJCX2UPW7ugymozqbV55WbIj1c96XPpQ,65038
|
|
150
150
|
sqlalchemy/orm/decl_base.py,sha256=N13zJJ0Yejcwu0yOWz8WI38ab56WTeHioYr2PlRCal0,83486
|
|
151
151
|
sqlalchemy/orm/dependency.py,sha256=eiYTsSnW94uGXEFQWj6-KFn25ivz_a2dPN3P6_nMou4,47619
|
|
152
|
-
sqlalchemy/orm/descriptor_props.py,sha256=
|
|
152
|
+
sqlalchemy/orm/descriptor_props.py,sha256=dh97zKu5-OHDNEhHA3H2YHwdpT8wVT06faeHDzED4pk,37795
|
|
153
153
|
sqlalchemy/orm/dynamic.py,sha256=Z4GpcVL8rM8gi0bytQOZXw-_kKi-sExbRWGjU30dK3g,9816
|
|
154
154
|
sqlalchemy/orm/evaluator.py,sha256=PKrUW1zEOvmv1XEgc_hBdYqNcyk4zjWr_rJhCEQBFIc,12353
|
|
155
155
|
sqlalchemy/orm/events.py,sha256=rdqxmaiaZ7MZ5LQwY5cz6irLkGpJzr1C66zkTsW-QgA,127780
|
|
@@ -157,12 +157,12 @@ sqlalchemy/orm/exc.py,sha256=V7cUPl9Kw4qZHLyjOvU1C5WMJ-0MKpNN10qM0C0YG5Y,7636
|
|
|
157
157
|
sqlalchemy/orm/identity.py,sha256=5NFtF9ZPZWAOmtOqCPyVX2-_pQq9A5XeN2ns3Wirpv8,9249
|
|
158
158
|
sqlalchemy/orm/instrumentation.py,sha256=WhElvvOWOn3Fuc-Asc5HmcKDX6EzFtBleLJKPZEc5A0,24321
|
|
159
159
|
sqlalchemy/orm/interfaces.py,sha256=C0RL0aOVB7E14EVp7MD9C55F2yrOfuOMZ0X-oZg3FCg,49072
|
|
160
|
-
sqlalchemy/orm/loading.py,sha256=
|
|
160
|
+
sqlalchemy/orm/loading.py,sha256=SMv9Q5bC-kdvsBpOqBNGqNWlL3I75fxByUeEpLC3qtg,58488
|
|
161
161
|
sqlalchemy/orm/mapped_collection.py,sha256=FAqaTlOUCYqdws2KR_fW0T8mMWIrLuAxJGU5f4W1aGs,19682
|
|
162
162
|
sqlalchemy/orm/mapper.py,sha256=-7q3rHqj3x_acv6prq3sDEXZmHx7kGSV9G-gW_JwaX4,171834
|
|
163
163
|
sqlalchemy/orm/path_registry.py,sha256=tRk3osC5BmU7kkcKJCeeibpg2witjyVzO0rX0pu8vmc,25914
|
|
164
|
-
sqlalchemy/orm/persistence.py,sha256=
|
|
165
|
-
sqlalchemy/orm/properties.py,sha256=
|
|
164
|
+
sqlalchemy/orm/persistence.py,sha256=laKaHW7XsVDYhXfDLnxqAJ5lPB8vhUZ0lEhLvtx-fb4,61812
|
|
165
|
+
sqlalchemy/orm/properties.py,sha256=yXxd40V25FIF9vSEev-AxH58yZie8mZMCGQtgFmoUe8,30127
|
|
166
166
|
sqlalchemy/orm/query.py,sha256=hPLslLL50lThw--5G8l3GtPgEdIY07hqIDOEO-0-wT8,118724
|
|
167
167
|
sqlalchemy/orm/relationships.py,sha256=t3yqixZ41chMVOnmelNaps7jwj5vwN9dZFSB0gKK9Pw,128763
|
|
168
168
|
sqlalchemy/orm/scoping.py,sha256=I_-BL8xAFQsZraFtA1wf5wgZ1WywBwBk-9OwiSAjPTM,78600
|
|
@@ -198,7 +198,7 @@ sqlalchemy/sql/dml.py,sha256=hUubKQK2dT91uMXyWuK1OpdJ6L4R_VyBw_rKH82lt7U,66232
|
|
|
198
198
|
sqlalchemy/sql/elements.py,sha256=E0lCoqQJbWwQ34xdhdxGXqGcFgrvla_xrnSpWgs4Uwo,178317
|
|
199
199
|
sqlalchemy/sql/events.py,sha256=iWjc_nm1vClDBLg4ZhDnY75CkBdnlDPSPe0MGBSmbiM,18312
|
|
200
200
|
sqlalchemy/sql/expression.py,sha256=CsOkmAQgaB-Rnwe7eK60FdBC5R9kY5pczCGrVw2BwGs,7583
|
|
201
|
-
sqlalchemy/sql/functions.py,sha256=
|
|
201
|
+
sqlalchemy/sql/functions.py,sha256=DQkV7asOlWaBtFTqRIC663oNkloy5EUhHexjo87GtUY,64826
|
|
202
202
|
sqlalchemy/sql/lambdas.py,sha256=W5b75ojie3EOm7poR27qsnQHQYdz-NxfSrgb5ATT2H0,49401
|
|
203
203
|
sqlalchemy/sql/naming.py,sha256=5Tk6nm4xqy8d9gzXzDvdiqqS7IptUaf1d7IuVdslplU,6855
|
|
204
204
|
sqlalchemy/sql/operators.py,sha256=h5bgu31gukGdsYsN_0-1C7IGAdSCFpBxuRjOUnu1Two,76792
|
|
@@ -221,7 +221,7 @@ sqlalchemy/testing/exclusions.py,sha256=0Byf3DIMQXN0-HOS6M2MPJ-fOm_n5MzE1yIfHgE0
|
|
|
221
221
|
sqlalchemy/testing/pickleable.py,sha256=G3L0xL9OtbX7wThfreRjWd0GW7q0kUKcTUuCN5ETGno,2833
|
|
222
222
|
sqlalchemy/testing/profiling.py,sha256=SWhWiZImJvDsNn0rQyNki70xdNxZL53ZI98ihxiykbQ,10148
|
|
223
223
|
sqlalchemy/testing/provision.py,sha256=6r2FTnm-t7u8MMbWo7eMhAH3qkL0w0WlmE29MUSEIu4,14702
|
|
224
|
-
sqlalchemy/testing/requirements.py,sha256=
|
|
224
|
+
sqlalchemy/testing/requirements.py,sha256=3u8lfzSOLE-_QUD6iHkhzRRbXDyEucmz2T8VRO8QG08,55757
|
|
225
225
|
sqlalchemy/testing/schema.py,sha256=IImFumAdpzOyoKAs0WnaGakq8D3sSU4snD9W4LVOV3s,6513
|
|
226
226
|
sqlalchemy/testing/util.py,sha256=BuA4q-8cmNhrUVqPP35Rr15MnYGSjmW0hmUdS1SI0_I,14526
|
|
227
227
|
sqlalchemy/testing/warnings.py,sha256=sj4vfTtjodcfoX6FPH_Zykb4fomjmgqIYj81QPpSwH8,1546
|
|
@@ -238,7 +238,7 @@ sqlalchemy/testing/suite/__init__.py,sha256=S8TLwTiif8xX67qlZUo5I9fl9UjZAFGSzvlp
|
|
|
238
238
|
sqlalchemy/testing/suite/test_cte.py,sha256=_GnADXRnhm37RdSRBR5SthQenTeb5VVo3HoCuO0Vifw,7262
|
|
239
239
|
sqlalchemy/testing/suite/test_ddl.py,sha256=MItp-votCzvahlRqHRagte2Omyq9XUOFdFsgzCb6_-g,12031
|
|
240
240
|
sqlalchemy/testing/suite/test_deprecations.py,sha256=7C6IbxRmq7wg_DLq56f1V5RCS9iVrAv3epJZQTB-dOo,5337
|
|
241
|
-
sqlalchemy/testing/suite/test_dialect.py,sha256=
|
|
241
|
+
sqlalchemy/testing/suite/test_dialect.py,sha256=j3srr7k2aUd_kPtJPgqI1g1aYD6ko4MvuGu1a1HQgS8,24215
|
|
242
242
|
sqlalchemy/testing/suite/test_insert.py,sha256=pR0VWMQ9JJPbnANE6634PzR0VFmWMF8im6OTahc4vsQ,18824
|
|
243
243
|
sqlalchemy/testing/suite/test_reflection.py,sha256=oRqwm8ZUjDdXcE3mooIg5513FpNiwEl76IoJaa_aK-Q,114101
|
|
244
244
|
sqlalchemy/testing/suite/test_results.py,sha256=S7Vqqh_Wuqf7uhM8h0cBVeV1GS5GJRO_ZTVYmT7kwuc,17042
|
|
@@ -262,8 +262,8 @@ sqlalchemy/util/queue.py,sha256=w1ufhuiC7lzyiZDhciRtRz1uyxU72jRI7SWhhL-p600,1018
|
|
|
262
262
|
sqlalchemy/util/tool_support.py,sha256=e7lWu6o1QlKq4e6c9PyDsuyFyiWe79vO72UQ_YX2pUA,6135
|
|
263
263
|
sqlalchemy/util/topological.py,sha256=tbkMRY0TTgNiq44NUJpnazXR4xb9v4Q4mQ8BygMp0vY,3451
|
|
264
264
|
sqlalchemy/util/typing.py,sha256=iwyZIgOJUN2o9cRz8YTH093iY5iNvpXiDQG3pce0cc4,22466
|
|
265
|
-
sqlalchemy-2.0.
|
|
266
|
-
sqlalchemy-2.0.
|
|
267
|
-
sqlalchemy-2.0.
|
|
268
|
-
sqlalchemy-2.0.
|
|
269
|
-
sqlalchemy-2.0.
|
|
265
|
+
sqlalchemy-2.0.43.dist-info/licenses/LICENSE,sha256=mCFyC1jUpWW2EyEAeorUOraZGjlZ5mzV203Z6uacffw,1100
|
|
266
|
+
sqlalchemy-2.0.43.dist-info/METADATA,sha256=6StIsiY_vKcG9DPqObgaUSVms9cc12bYmm3KbXl9yMw,9577
|
|
267
|
+
sqlalchemy-2.0.43.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
268
|
+
sqlalchemy-2.0.43.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
|
|
269
|
+
sqlalchemy-2.0.43.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|