SQLAlchemy 2.1.0rc2__py3-none-any.whl → 2.1.1__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/dialects/mssql/mssqlpython.py +13 -4
- sqlalchemy/dialects/oracle/oracledb.py +24 -1
- sqlalchemy/dialects/postgresql/base.py +44 -2
- sqlalchemy/engine/interfaces.py +12 -1
- sqlalchemy/orm/_orm_constructors.py +17 -0
- sqlalchemy/orm/descriptor_props.py +24 -2
- sqlalchemy/orm/strategies.py +10 -3
- sqlalchemy/orm/util.py +93 -4
- sqlalchemy/sql/base.py +151 -1
- sqlalchemy/sql/schema.py +5 -0
- sqlalchemy/sql/selectable.py +19 -9
- sqlalchemy/testing/requirements.py +19 -0
- sqlalchemy/testing/suite/test_dialect.py +1 -0
- sqlalchemy/testing/suite/test_update_delete.py +37 -0
- sqlalchemy/util/langhelpers.py +7 -1
- {sqlalchemy-2.1.0rc2.dist-info → sqlalchemy-2.1.1.dist-info}/METADATA +1 -23
- {sqlalchemy-2.1.0rc2.dist-info → sqlalchemy-2.1.1.dist-info}/RECORD +22 -22
- {sqlalchemy-2.1.0rc2.dist-info → sqlalchemy-2.1.1.dist-info}/WHEEL +0 -0
- {sqlalchemy-2.1.0rc2.dist-info → sqlalchemy-2.1.1.dist-info}/licenses/AUTHORS +0 -0
- {sqlalchemy-2.1.0rc2.dist-info → sqlalchemy-2.1.1.dist-info}/licenses/LICENSE +0 -0
- {sqlalchemy-2.1.0rc2.dist-info → sqlalchemy-2.1.1.dist-info}/top_level.txt +0 -0
sqlalchemy/__init__.py
CHANGED
|
@@ -101,9 +101,6 @@ class MSDialect_mssqlpython(MSDialect):
|
|
|
101
101
|
}
|
|
102
102
|
)
|
|
103
103
|
|
|
104
|
-
# used by pyodbc _ms_numeric_pyodbc class
|
|
105
|
-
_need_decimal_fix = True
|
|
106
|
-
|
|
107
104
|
colspecs = util.update_copy(
|
|
108
105
|
MSDialect.colspecs,
|
|
109
106
|
{
|
|
@@ -117,6 +114,18 @@ class MSDialect_mssqlpython(MSDialect):
|
|
|
117
114
|
if not enable_pooling and self.dbapi is not None:
|
|
118
115
|
self.loaded_dbapi.pooling(enabled=False)
|
|
119
116
|
|
|
117
|
+
# used by pyodbc _ms_numeric_pyodbc class. mssql-python 1.15
|
|
118
|
+
# binds every Decimal as SQL_NUMERIC; prior versions bound Decimal
|
|
119
|
+
# values within the MONEY range as VARCHAR, so that very large and
|
|
120
|
+
# very small values needed to be passed as strings as well. On
|
|
121
|
+
# 1.15 those strings are instead coerced to the NUMERIC type of
|
|
122
|
+
# the Decimal values alongside them, which overflows.
|
|
123
|
+
# a version which can't be determined is assumed to be an old one
|
|
124
|
+
version = self._dbapi_version_or_none
|
|
125
|
+
self._need_decimal_fix = bool(self.dbapi) and (
|
|
126
|
+
version is None or version < (1, 15)
|
|
127
|
+
)
|
|
128
|
+
|
|
120
129
|
@classmethod
|
|
121
130
|
def import_dbapi(cls) -> DBAPIModule:
|
|
122
131
|
return __import__("mssql_python")
|
|
@@ -204,7 +213,7 @@ class MSDialect_mssqlpython(MSDialect):
|
|
|
204
213
|
def retrieve_dbapi_version(
|
|
205
214
|
self, dbapi: interfaces.DBAPIModule
|
|
206
215
|
) -> util.VersionInfo:
|
|
207
|
-
return util.parse_version_string(dbapi
|
|
216
|
+
return util.parse_version_string(getattr(dbapi, "__version__", None))
|
|
208
217
|
|
|
209
218
|
def _get_server_version_info(self, connection):
|
|
210
219
|
vers = connection.exec_driver_sql("select @@version").scalar()
|
|
@@ -600,6 +600,7 @@ from ...connectors.asyncio import AsyncAdapt_dbapi_connection
|
|
|
600
600
|
from ...connectors.asyncio import AsyncAdapt_dbapi_cursor
|
|
601
601
|
from ...connectors.asyncio import AsyncAdapt_dbapi_module
|
|
602
602
|
from ...connectors.asyncio import AsyncAdapt_dbapi_ss_cursor
|
|
603
|
+
from ...connectors.asyncio import AsyncAdapt_terminate
|
|
603
604
|
from ...engine import default
|
|
604
605
|
from ...util import await_
|
|
605
606
|
|
|
@@ -773,7 +774,9 @@ class AsyncAdapt_oracledb_ss_cursor(
|
|
|
773
774
|
self._cursor = None # type: ignore
|
|
774
775
|
|
|
775
776
|
|
|
776
|
-
class AsyncAdapt_oracledb_connection(
|
|
777
|
+
class AsyncAdapt_oracledb_connection(
|
|
778
|
+
AsyncAdapt_terminate, AsyncAdapt_dbapi_connection
|
|
779
|
+
):
|
|
777
780
|
_connection: AsyncConnection
|
|
778
781
|
__slots__ = ()
|
|
779
782
|
|
|
@@ -838,6 +841,12 @@ class AsyncAdapt_oracledb_connection(AsyncAdapt_dbapi_connection):
|
|
|
838
841
|
def tpc_rollback(self, *args: Any, **kwargs: Any) -> Any:
|
|
839
842
|
return await_(self._connection.tpc_rollback(*args, **kwargs))
|
|
840
843
|
|
|
844
|
+
async def _terminate_graceful_close(self) -> None:
|
|
845
|
+
await self._connection.close()
|
|
846
|
+
|
|
847
|
+
def _terminate_force_close(self) -> None:
|
|
848
|
+
self._connection.terminate()
|
|
849
|
+
|
|
841
850
|
|
|
842
851
|
class OracledbAdaptDBAPI(AsyncAdapt_dbapi_module):
|
|
843
852
|
def __init__(self, oracledb) -> None:
|
|
@@ -883,6 +892,14 @@ class OracleDialectAsync_oracledb(OracleDialect_oracledb):
|
|
|
883
892
|
|
|
884
893
|
minimum_dbapi_version = util.VersionInfo((2, 0, 1))
|
|
885
894
|
|
|
895
|
+
def __init__(self, **kwargs):
|
|
896
|
+
super().__init__(**kwargs)
|
|
897
|
+
|
|
898
|
+
self.has_terminate = (
|
|
899
|
+
self._dbapi_version_or_none is not None
|
|
900
|
+
and self._dbapi_version_or_none >= (26, 0, 0)
|
|
901
|
+
)
|
|
902
|
+
|
|
886
903
|
# thick_mode mode is not supported by asyncio, oracledb will raise
|
|
887
904
|
@classmethod
|
|
888
905
|
def import_dbapi(cls):
|
|
@@ -893,6 +910,12 @@ class OracleDialectAsync_oracledb(OracleDialect_oracledb):
|
|
|
893
910
|
def get_driver_connection(self, connection):
|
|
894
911
|
return connection._connection
|
|
895
912
|
|
|
913
|
+
def do_terminate(self, dbapi_connection) -> None:
|
|
914
|
+
if self.has_terminate:
|
|
915
|
+
dbapi_connection.terminate()
|
|
916
|
+
else:
|
|
917
|
+
super().do_terminate(dbapi_connection)
|
|
918
|
+
|
|
896
919
|
|
|
897
920
|
dialect = OracleDialect_oracledb
|
|
898
921
|
dialect_async = OracleDialectAsync_oracledb
|
|
@@ -1179,6 +1179,12 @@ construct, the DBAPI's "autocommit" mode must be used::
|
|
|
1179
1179
|
PostgreSQL Index Reflection
|
|
1180
1180
|
---------------------------
|
|
1181
1181
|
|
|
1182
|
+
Notes on Index reflection
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
Implicit UNIQUE index for UNIQUE CONSTRAINT
|
|
1186
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
1187
|
+
|
|
1182
1188
|
The PostgreSQL database creates a UNIQUE INDEX implicitly whenever the
|
|
1183
1189
|
UNIQUE CONSTRAINT construct is used. When inspecting a table using
|
|
1184
1190
|
:class:`_reflection.Inspector`, the :meth:`_reflection.Inspector.get_indexes`
|
|
@@ -1189,8 +1195,38 @@ two constructs distinctly; in the case of the index, the key
|
|
|
1189
1195
|
detected as mirroring a constraint. When performing reflection using
|
|
1190
1196
|
``Table(..., autoload_with=engine)``, the UNIQUE INDEX is **not** returned
|
|
1191
1197
|
in :attr:`_schema.Table.indexes` when it is detected as mirroring a
|
|
1192
|
-
:class:`.UniqueConstraint` in the :attr:`_schema.Table.constraints` collection
|
|
1193
|
-
|
|
1198
|
+
:class:`.UniqueConstraint` in the :attr:`_schema.Table.constraints` collection.
|
|
1199
|
+
|
|
1200
|
+
.. _postgresql_invalid_index_reflection:
|
|
1201
|
+
|
|
1202
|
+
Invalid indexes
|
|
1203
|
+
^^^^^^^^^^^^^^^
|
|
1204
|
+
|
|
1205
|
+
Indexes marked "invalid" by PostgreSQL (i.e. an index that exists in the
|
|
1206
|
+
catalog, but is ignored by the query planner because it may be incomplete,
|
|
1207
|
+
while still being maintained on writes) remain present in reflection. The
|
|
1208
|
+
:meth:`_reflection.Inspector.get_indexes` and
|
|
1209
|
+
:meth:`_reflection.Inspector.get_multi_indexes` methods include
|
|
1210
|
+
``postgresql_invalid=True`` in the index's ``dialect_options`` dictionary when
|
|
1211
|
+
``pg_index.indisvalid`` is false. Valid indexes omit this flag.
|
|
1212
|
+
|
|
1213
|
+
When reflecting a :class:`_schema.Table`, the flag, if present, is stored
|
|
1214
|
+
separately from DDL options on each reflected :class:`.Index`::
|
|
1215
|
+
|
|
1216
|
+
table = Table("my_table", MetaData(), autoload_with=engine)
|
|
1217
|
+
for index in table.indexes:
|
|
1218
|
+
if index.reflect_only_elements["postgresql"].get("invalid"):
|
|
1219
|
+
print(index.name)
|
|
1220
|
+
|
|
1221
|
+
The :attr:`.Index.reflect_only_elements` mapping is read-only. These values are
|
|
1222
|
+
not included in :attr:`.Index.dialect_kwargs` and do not affect DDL.
|
|
1223
|
+
|
|
1224
|
+
An invalid index may be left by a failed ``CREATE INDEX CONCURRENTLY``, may
|
|
1225
|
+
still be building, or may be a partitioned index awaiting attachment of its
|
|
1226
|
+
partition indexes. This flag reports the state at reflection time; it does not
|
|
1227
|
+
request an index rebuild.
|
|
1228
|
+
|
|
1229
|
+
.. versionadded:: 2.1
|
|
1194
1230
|
|
|
1195
1231
|
Special Reflection Options
|
|
1196
1232
|
--------------------------
|
|
@@ -1946,6 +1982,7 @@ from ...sql import functions
|
|
|
1946
1982
|
from ...sql import roles
|
|
1947
1983
|
from ...sql import sqltypes
|
|
1948
1984
|
from ...sql import util as sql_util
|
|
1985
|
+
from ...sql.base import DialectKWArgConst
|
|
1949
1986
|
from ...sql.compiler import InsertmanyvaluesSentinelOpts
|
|
1950
1987
|
from ...sql.visitors import InternalTraversal
|
|
1951
1988
|
from ...types import BIGINT
|
|
@@ -3666,6 +3703,7 @@ class PGDialect(default._BackendsMultiReflection, default.DefaultDialect):
|
|
|
3666
3703
|
"with": {},
|
|
3667
3704
|
"tablespace": None,
|
|
3668
3705
|
"nulls_not_distinct": None,
|
|
3706
|
+
"invalid": DialectKWArgConst.REFLECTED_ONLY,
|
|
3669
3707
|
},
|
|
3670
3708
|
),
|
|
3671
3709
|
(
|
|
@@ -5269,6 +5307,7 @@ class PGDialect(default._BackendsMultiReflection, default.DefaultDialect):
|
|
|
5269
5307
|
pg_catalog.pg_index.c.indrelid,
|
|
5270
5308
|
pg_catalog.pg_class.c.relname,
|
|
5271
5309
|
pg_catalog.pg_index.c.indisunique,
|
|
5310
|
+
pg_catalog.pg_index.c.indisvalid,
|
|
5272
5311
|
pg_catalog.pg_constraint.c.conrelid.is_not(None).label(
|
|
5273
5312
|
"has_constraint"
|
|
5274
5313
|
),
|
|
@@ -5475,6 +5514,9 @@ class PGDialect(default._BackendsMultiReflection, default.DefaultDialect):
|
|
|
5475
5514
|
"indnullsnotdistinct"
|
|
5476
5515
|
]
|
|
5477
5516
|
|
|
5517
|
+
if not row["indisvalid"]:
|
|
5518
|
+
dialect_options["postgresql_invalid"] = True
|
|
5519
|
+
|
|
5478
5520
|
if dialect_options:
|
|
5479
5521
|
index["dialect_options"] = dialect_options
|
|
5480
5522
|
|
sqlalchemy/engine/interfaces.py
CHANGED
|
@@ -560,7 +560,7 @@ class ReflectedIndex(TypedDict):
|
|
|
560
560
|
"""
|
|
561
561
|
|
|
562
562
|
dialect_options: NotRequired[Dict[str, Any]]
|
|
563
|
-
"""Additional dialect-specific options detected for this index"""
|
|
563
|
+
"""Additional dialect-specific options detected for this index."""
|
|
564
564
|
|
|
565
565
|
|
|
566
566
|
class ReflectedTableComment(TypedDict):
|
|
@@ -1259,6 +1259,17 @@ class Dialect(EventTarget):
|
|
|
1259
1259
|
here is so that third-party dialects that haven't yet implemented this
|
|
1260
1260
|
feature continue to function in the old way.
|
|
1261
1261
|
|
|
1262
|
+
Arguments that represent database state without any corresponding DDL,
|
|
1263
|
+
but may nonetheless appear in a reflected set of arguments that should
|
|
1264
|
+
be ignored by the construct, may make use of the
|
|
1265
|
+
:attr:`.DialectKWArgConst.REFLECTED_ONLY` token as the default value.
|
|
1266
|
+
Arguments passed to the schema object on such a key will be passed along
|
|
1267
|
+
into the separate read-only collection
|
|
1268
|
+
:attr:`.DialectKWArgs.reflect_only_elements` and should be omitted from
|
|
1269
|
+
DDL rendering.
|
|
1270
|
+
|
|
1271
|
+
.. versionadded:: 2.1 Added :class:`.DialectKWArgConst`.
|
|
1272
|
+
|
|
1262
1273
|
.. seealso::
|
|
1263
1274
|
|
|
1264
1275
|
:class:`.DialectKWArgs` - implementing base class which consumes
|
|
@@ -642,6 +642,7 @@ def composite(
|
|
|
642
642
|
return_none_on: Union[_NoArg, None, Callable[..., bool]] = _NoArg.NO_ARG,
|
|
643
643
|
comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None,
|
|
644
644
|
active_history: bool = False,
|
|
645
|
+
column_template: Optional[str] = None,
|
|
645
646
|
init: Union[_NoArg, bool] = _NoArg.NO_ARG,
|
|
646
647
|
repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
|
|
647
648
|
default: Optional[Any] = _NoArg.NO_ARG,
|
|
@@ -667,6 +668,7 @@ def composite(
|
|
|
667
668
|
return_none_on: Union[_NoArg, None, Callable[..., bool]] = _NoArg.NO_ARG,
|
|
668
669
|
comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None,
|
|
669
670
|
active_history: bool = False,
|
|
671
|
+
column_template: Optional[str] = None,
|
|
670
672
|
init: Union[_NoArg, bool] = _NoArg.NO_ARG,
|
|
671
673
|
repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
|
|
672
674
|
default: Optional[Any] = _NoArg.NO_ARG,
|
|
@@ -691,6 +693,7 @@ def composite(
|
|
|
691
693
|
return_none_on: Union[_NoArg, None, Callable[..., bool]] = _NoArg.NO_ARG,
|
|
692
694
|
comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None,
|
|
693
695
|
active_history: bool = False,
|
|
696
|
+
column_template: Optional[str] = None,
|
|
694
697
|
init: Union[_NoArg, bool] = _NoArg.NO_ARG,
|
|
695
698
|
repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
|
|
696
699
|
default: Optional[Any] = _NoArg.NO_ARG,
|
|
@@ -716,6 +719,7 @@ def composite(
|
|
|
716
719
|
return_none_on: Union[_NoArg, None, Callable[..., bool]] = _NoArg.NO_ARG,
|
|
717
720
|
comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None,
|
|
718
721
|
active_history: bool = False,
|
|
722
|
+
column_template: Optional[str] = None,
|
|
719
723
|
init: Union[_NoArg, bool] = _NoArg.NO_ARG,
|
|
720
724
|
repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
|
|
721
725
|
default: Optional[Any] = _NoArg.NO_ARG,
|
|
@@ -785,6 +789,18 @@ def composite(
|
|
|
785
789
|
:class:`.Composite.Comparator` which provides custom SQL
|
|
786
790
|
clause generation for comparison operations.
|
|
787
791
|
|
|
792
|
+
:param column_template: A string template such as ``"person_%s"``,
|
|
793
|
+
containing exactly one ``%s`` placeholder, that's used to generate
|
|
794
|
+
column names for fields of a dataclass :paramref:`.composite.class_`
|
|
795
|
+
that don't otherwise have an explicit name. Only supported for
|
|
796
|
+
dataclass-based composite classes.
|
|
797
|
+
|
|
798
|
+
.. seealso::
|
|
799
|
+
|
|
800
|
+
:ref:`composite_column_template`
|
|
801
|
+
|
|
802
|
+
.. versionadded:: 2.1
|
|
803
|
+
|
|
788
804
|
:param doc:
|
|
789
805
|
optional string that will be applied as the doc on the
|
|
790
806
|
class-bound descriptor.
|
|
@@ -852,6 +868,7 @@ def composite(
|
|
|
852
868
|
raiseload=raiseload,
|
|
853
869
|
comparator_factory=comparator_factory,
|
|
854
870
|
active_history=active_history,
|
|
871
|
+
column_template=column_template,
|
|
855
872
|
info=info,
|
|
856
873
|
doc=doc,
|
|
857
874
|
)
|
|
@@ -208,6 +208,7 @@ class CompositeProperty(
|
|
|
208
208
|
|
|
209
209
|
composite_class: Union[Type[_CC], Callable[..., _CC]]
|
|
210
210
|
attrs: Tuple[_CompositeAttrType[Any], ...]
|
|
211
|
+
column_template: Optional[str]
|
|
211
212
|
|
|
212
213
|
_generated_composite_accessor: CallableReference[
|
|
213
214
|
Optional[Callable[[_CC], Tuple[Any, ...]]]
|
|
@@ -229,6 +230,7 @@ class CompositeProperty(
|
|
|
229
230
|
deferred: bool = False,
|
|
230
231
|
group: Optional[str] = None,
|
|
231
232
|
comparator_factory: Optional[Type[Comparator[_CC]]] = None,
|
|
233
|
+
column_template: Optional[str] = None,
|
|
232
234
|
info: Optional[_InfoType] = None,
|
|
233
235
|
**kwargs: Any,
|
|
234
236
|
):
|
|
@@ -242,6 +244,17 @@ class CompositeProperty(
|
|
|
242
244
|
self.composite_class = _class_or_attr # type: ignore[assignment]
|
|
243
245
|
self.attrs = attrs
|
|
244
246
|
|
|
247
|
+
if column_template is not None:
|
|
248
|
+
try:
|
|
249
|
+
column_template % "x"
|
|
250
|
+
except (TypeError, ValueError) as te:
|
|
251
|
+
raise sa_exc.ArgumentError(
|
|
252
|
+
f"column_template {column_template!r} is not a valid "
|
|
253
|
+
"template; expected a string containing exactly one "
|
|
254
|
+
"'%s' placeholder"
|
|
255
|
+
) from te
|
|
256
|
+
self.column_template = column_template
|
|
257
|
+
|
|
245
258
|
self.return_none_on = return_none_on
|
|
246
259
|
self.active_history = active_history
|
|
247
260
|
self.deferred = deferred
|
|
@@ -428,6 +441,11 @@ class CompositeProperty(
|
|
|
428
441
|
decl_scan, registry, cls, originating_module, key
|
|
429
442
|
)
|
|
430
443
|
else:
|
|
444
|
+
if self.column_template is not None:
|
|
445
|
+
raise sa_exc.ArgumentError(
|
|
446
|
+
"column_template is only supported when composite_class "
|
|
447
|
+
"is a dataclass"
|
|
448
|
+
)
|
|
431
449
|
for attr in self.attrs:
|
|
432
450
|
if (
|
|
433
451
|
isinstance(attr, (MappedColumn, schema.Column))
|
|
@@ -492,8 +510,12 @@ class CompositeProperty(
|
|
|
492
510
|
f"{self.composite_class.__name__} {len(insp.parameters)}"
|
|
493
511
|
)
|
|
494
512
|
if attr is None:
|
|
495
|
-
# fill in missing attr spots with empty MappedColumn
|
|
496
|
-
|
|
513
|
+
# fill in missing attr spots with empty MappedColumn,
|
|
514
|
+
# or one named from column_template if present
|
|
515
|
+
if self.column_template is not None:
|
|
516
|
+
attr = MappedColumn(self.column_template % param.name)
|
|
517
|
+
else:
|
|
518
|
+
attr = MappedColumn()
|
|
497
519
|
self.attrs += (attr,)
|
|
498
520
|
|
|
499
521
|
if isinstance(attr, MappedColumn):
|
sqlalchemy/orm/strategies.py
CHANGED
|
@@ -66,6 +66,13 @@ if TYPE_CHECKING:
|
|
|
66
66
|
from ..sql.elements import ColumnElement
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
# sentinel for _LoadLazyAttribute.extra_criteria indicating that the loader
|
|
70
|
+
# option had no additional criteria. the attribute is otherwise a single
|
|
71
|
+
# element returned by Load._generate_extra_criteria(), which has no
|
|
72
|
+
# meaningful boolean value of its own.
|
|
73
|
+
_NO_EXTRA_CRITERIA = None
|
|
74
|
+
|
|
75
|
+
|
|
69
76
|
def _register_attribute(
|
|
70
77
|
prop,
|
|
71
78
|
mapper,
|
|
@@ -1233,7 +1240,7 @@ class _LazyLoader(
|
|
|
1233
1240
|
(
|
|
1234
1241
|
loadopt._generate_extra_criteria(context)
|
|
1235
1242
|
if loadopt._extra_criteria
|
|
1236
|
-
else
|
|
1243
|
+
else _NO_EXTRA_CRITERIA
|
|
1237
1244
|
),
|
|
1238
1245
|
),
|
|
1239
1246
|
key,
|
|
@@ -1281,7 +1288,7 @@ class _LoadLazyAttribute:
|
|
|
1281
1288
|
self.extra_criteria = extra_criteria
|
|
1282
1289
|
|
|
1283
1290
|
def __getstate__(self):
|
|
1284
|
-
if self.extra_criteria is not
|
|
1291
|
+
if self.extra_criteria is not _NO_EXTRA_CRITERIA:
|
|
1285
1292
|
util.warn(
|
|
1286
1293
|
"Can't reliably serialize a lazyload() option that "
|
|
1287
1294
|
"contains additional criteria; please use eager loading "
|
|
@@ -1291,7 +1298,7 @@ class _LoadLazyAttribute:
|
|
|
1291
1298
|
"key": self.key,
|
|
1292
1299
|
"strategy_key": self.strategy_key,
|
|
1293
1300
|
"loadopt": self.loadopt,
|
|
1294
|
-
"extra_criteria":
|
|
1301
|
+
"extra_criteria": _NO_EXTRA_CRITERIA,
|
|
1295
1302
|
}
|
|
1296
1303
|
|
|
1297
1304
|
def __call__(self, state, passive=attributes.PASSIVE_OFF):
|
sqlalchemy/orm/util.py
CHANGED
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import enum
|
|
12
12
|
import functools
|
|
13
|
+
import itertools
|
|
13
14
|
import re
|
|
14
15
|
import types
|
|
15
16
|
import typing
|
|
@@ -633,6 +634,41 @@ class ORMAdapter(sql_util.ColumnAdapter):
|
|
|
633
634
|
return not entity or entity.isa(self.mapper) or self.mapper.isa(entity)
|
|
634
635
|
|
|
635
636
|
|
|
637
|
+
def _is_alias_of_selectable(
|
|
638
|
+
selectable: FromClause, target: FromClause
|
|
639
|
+
) -> bool:
|
|
640
|
+
"""Return True if ``selectable`` is ``target``, or a plain alias of it,
|
|
641
|
+
including a flat alias of a join of the same structure and join types."""
|
|
642
|
+
|
|
643
|
+
if selectable is target or (
|
|
644
|
+
isinstance(selectable, expression.Alias)
|
|
645
|
+
and selectable.element is target
|
|
646
|
+
):
|
|
647
|
+
return True
|
|
648
|
+
|
|
649
|
+
return all(
|
|
650
|
+
elem is tgt
|
|
651
|
+
or (isinstance(elem, expression.Alias) and elem.element is tgt)
|
|
652
|
+
or (
|
|
653
|
+
# zip_longest() pads the shorter of the two with None
|
|
654
|
+
elem is not None
|
|
655
|
+
and tgt is not None
|
|
656
|
+
and elem._is_join
|
|
657
|
+
and tgt._is_join
|
|
658
|
+
and elem.isouter == tgt.isouter
|
|
659
|
+
and elem.full == tgt.full
|
|
660
|
+
)
|
|
661
|
+
or (
|
|
662
|
+
isinstance(elem, expression.FromGrouping)
|
|
663
|
+
and isinstance(tgt, expression.FromGrouping)
|
|
664
|
+
)
|
|
665
|
+
for elem, tgt in itertools.zip_longest(
|
|
666
|
+
sql_util.surface_selectables(selectable),
|
|
667
|
+
sql_util.surface_selectables(target),
|
|
668
|
+
)
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
|
|
636
672
|
class AliasedClass(
|
|
637
673
|
inspection.Inspectable["AliasedInsp[_O]"], ORMColumnsClauseRole[_O]
|
|
638
674
|
):
|
|
@@ -718,9 +754,38 @@ class AliasedClass(
|
|
|
718
754
|
nest_adapters = False
|
|
719
755
|
|
|
720
756
|
if alias is None:
|
|
721
|
-
if insp
|
|
722
|
-
|
|
723
|
-
|
|
757
|
+
if insp_is_aliased_class(insp):
|
|
758
|
+
if insp._is_with_polymorphic:
|
|
759
|
+
# polymorphic arguments are only passed along with an
|
|
760
|
+
# explicit selectable, by with_polymorphic() and
|
|
761
|
+
# AliasedInsp._merge_with()
|
|
762
|
+
assert (
|
|
763
|
+
not with_polymorphic_mappers
|
|
764
|
+
and with_polymorphic_discriminator is None
|
|
765
|
+
and not represents_outer_join
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
# aliased() of a with_polymorphic() carries along its
|
|
769
|
+
# polymorphic configuration, #13584
|
|
770
|
+
with_polymorphic_mappers = insp.with_polymorphic_mappers
|
|
771
|
+
with_polymorphic_discriminator = insp.polymorphic_on
|
|
772
|
+
represents_outer_join = insp.represents_outer_join
|
|
773
|
+
|
|
774
|
+
if not _is_alias_of_selectable(
|
|
775
|
+
insp.selectable, mapper._with_polymorphic_selectable
|
|
776
|
+
):
|
|
777
|
+
# aliased() of an aliased() that refers to a subquery,
|
|
778
|
+
# CTE, with_polymorphic() or other selectable; alias
|
|
779
|
+
# that selectable rather than the mapped table,
|
|
780
|
+
# #13583, #13584
|
|
781
|
+
alias = insp.selectable._anonymous_fromclause(
|
|
782
|
+
name=name, flat=flat
|
|
783
|
+
)
|
|
784
|
+
adapt_on_names = adapt_on_names or insp._adapt_on_names
|
|
785
|
+
elif insp.selectable._is_subquery:
|
|
786
|
+
alias = insp.selectable.alias()
|
|
787
|
+
|
|
788
|
+
if alias is None:
|
|
724
789
|
alias = (
|
|
725
790
|
mapper._with_polymorphic_selectable._anonymous_fromclause(
|
|
726
791
|
name=name,
|
|
@@ -925,6 +990,7 @@ class AliasedInsp(
|
|
|
925
990
|
_adapter: ORMAdapter
|
|
926
991
|
with_polymorphic_mappers: Sequence[Mapper[Any]]
|
|
927
992
|
_with_polymorphic_entities: Sequence[AliasedInsp[Any]]
|
|
993
|
+
_adapt_on_names: bool
|
|
928
994
|
|
|
929
995
|
_weak_entity: weakref.ref[AliasedClass[_O]]
|
|
930
996
|
"""the AliasedClass that refers to this AliasedInsp"""
|
|
@@ -2344,6 +2410,29 @@ def _cleanup_mapped_str_annotation(
|
|
|
2344
2410
|
return annotation
|
|
2345
2411
|
|
|
2346
2412
|
|
|
2413
|
+
def _unresolved_annotation_is_mapped(
|
|
2414
|
+
raw_annotation: _AnnotationScanType,
|
|
2415
|
+
) -> bool:
|
|
2416
|
+
"""given an un-resolvable annotation, guess if it's ``Mapped[]``.
|
|
2417
|
+
|
|
2418
|
+
Used only to decide whether a ``NameError`` raised while de-stringifying
|
|
2419
|
+
is worth reporting as a mapping error. The annotation may be a plain
|
|
2420
|
+
string (``__future__`` annotations / explicitly quoted), a ``ForwardRef``
|
|
2421
|
+
(:pep:`649` deferred annotations on Python 3.14 and above), or an
|
|
2422
|
+
already-resolved object.
|
|
2423
|
+
|
|
2424
|
+
"""
|
|
2425
|
+
if isinstance(raw_annotation, str):
|
|
2426
|
+
return "Mapped[" in raw_annotation
|
|
2427
|
+
elif isinstance(raw_annotation, typing.ForwardRef):
|
|
2428
|
+
return "Mapped[" in raw_annotation.__forward_arg__
|
|
2429
|
+
else:
|
|
2430
|
+
origin = typing.get_origin(raw_annotation)
|
|
2431
|
+
return isinstance(origin, type) and issubclass(
|
|
2432
|
+
origin, _MappedAnnotationBase
|
|
2433
|
+
)
|
|
2434
|
+
|
|
2435
|
+
|
|
2347
2436
|
def _extract_mapped_subtype(
|
|
2348
2437
|
raw_annotation: Optional[_AnnotationScanType],
|
|
2349
2438
|
cls: type,
|
|
@@ -2390,7 +2479,7 @@ def _extract_mapped_subtype(
|
|
|
2390
2479
|
"module level. See chained stack trace for more hints."
|
|
2391
2480
|
) from ce
|
|
2392
2481
|
except NameError as ne:
|
|
2393
|
-
if raiseerr and
|
|
2482
|
+
if raiseerr and _unresolved_annotation_is_mapped(raw_annotation):
|
|
2394
2483
|
raise orm_exc.MappedAnnotationError(
|
|
2395
2484
|
f"Could not interpret annotation {raw_annotation}. "
|
|
2396
2485
|
"Check that it uses names that are correctly imported at the "
|
sqlalchemy/sql/base.py
CHANGED
|
@@ -377,6 +377,31 @@ def _cloned_difference(a: Iterable[_CLE], b: Iterable[_CLE]) -> Set[_CLE]:
|
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
|
|
380
|
+
class DialectKWArgConst(Enum):
|
|
381
|
+
"""Constants for dialect argument defaults in
|
|
382
|
+
:attr:`.DefaultDialect.construct_arguments`.
|
|
383
|
+
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
REFLECTED_ONLY = 1
|
|
387
|
+
"""Mark a dialect argument as database state reported by reflection.
|
|
388
|
+
|
|
389
|
+
Applies to any construct that takes part in
|
|
390
|
+
:attr:`.DefaultDialect.construct_arguments`, such as :class:`.Table`,
|
|
391
|
+
:class:`.Column`, :class:`.Index` or :class:`.CheckConstraint`. The
|
|
392
|
+
value is kept in the construct's read-only
|
|
393
|
+
:attr:`.DialectKWArgs.reflect_only_elements` mapping, separate from the
|
|
394
|
+
options used to generate DDL.
|
|
395
|
+
|
|
396
|
+
.. seealso::
|
|
397
|
+
|
|
398
|
+
:attr:`.DialectKWArgs.reflect_only_elements`
|
|
399
|
+
|
|
400
|
+
.. versionadded:: 2.1
|
|
401
|
+
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
|
|
380
405
|
class _DialectArgView(MutableMapping[str, Any]):
|
|
381
406
|
"""A dictionary view of dialect-level arguments in the form
|
|
382
407
|
<dialectname>_<argument_name>.
|
|
@@ -436,6 +461,46 @@ class _DialectArgView(MutableMapping[str, Any]):
|
|
|
436
461
|
)
|
|
437
462
|
|
|
438
463
|
|
|
464
|
+
class _ReflectOnlyView(Mapping[str, Mapping[str, Any]]):
|
|
465
|
+
"""A read-only dictionary view of reflection-only dialect-level
|
|
466
|
+
arguments, keyed to <dialectname>, then <argument_name>.
|
|
467
|
+
|
|
468
|
+
A dialect name that has no reflection-only arguments present returns
|
|
469
|
+
an empty mapping.
|
|
470
|
+
|
|
471
|
+
"""
|
|
472
|
+
|
|
473
|
+
__slots__ = ("obj",)
|
|
474
|
+
|
|
475
|
+
def __init__(self, obj: DialectKWArgs) -> None:
|
|
476
|
+
self.obj = obj
|
|
477
|
+
|
|
478
|
+
def __getitem__(self, key: str) -> Mapping[str, Any]:
|
|
479
|
+
# check membership first so that the lookup does not attempt
|
|
480
|
+
# to load a dialect
|
|
481
|
+
if key in self.obj.dialect_options:
|
|
482
|
+
return self.obj.dialect_options[key]._reflect_only_elements
|
|
483
|
+
else:
|
|
484
|
+
return EMPTY_DICT
|
|
485
|
+
|
|
486
|
+
def __contains__(self, key: object) -> bool:
|
|
487
|
+
return bool(
|
|
488
|
+
isinstance(key, str)
|
|
489
|
+
and key in self.obj.dialect_options
|
|
490
|
+
and self.obj.dialect_options[key]._reflect_only_elements
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
def __len__(self) -> int:
|
|
494
|
+
return sum(1 for _ in self)
|
|
495
|
+
|
|
496
|
+
def __iter__(self) -> Generator[str, None, None]:
|
|
497
|
+
return (
|
|
498
|
+
dialect_name
|
|
499
|
+
for dialect_name, args in self.obj.dialect_options.items()
|
|
500
|
+
if args._reflect_only_elements
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
|
|
439
504
|
class _DialectArgDict(MutableMapping[str, Any]):
|
|
440
505
|
"""A dictionary view of dialect-level arguments for a specific
|
|
441
506
|
dialect.
|
|
@@ -448,6 +513,10 @@ class _DialectArgDict(MutableMapping[str, Any]):
|
|
|
448
513
|
def __init__(self) -> None:
|
|
449
514
|
self._non_defaults: Dict[str, Any] = {}
|
|
450
515
|
self._defaults: Dict[str, Any] = {}
|
|
516
|
+
self._reflection_only_keys: FrozenSet[str] = util.EMPTY_SET
|
|
517
|
+
self._reflect_only_elements: util.immutabledict[str, Any] = (
|
|
518
|
+
util.EMPTY_DICT
|
|
519
|
+
)
|
|
451
520
|
|
|
452
521
|
def __len__(self) -> int:
|
|
453
522
|
return len(set(self._non_defaults).union(self._defaults))
|
|
@@ -462,7 +531,12 @@ class _DialectArgDict(MutableMapping[str, Any]):
|
|
|
462
531
|
return self._defaults[key]
|
|
463
532
|
|
|
464
533
|
def __setitem__(self, key: str, value: Any) -> None:
|
|
465
|
-
|
|
534
|
+
if key in self._reflection_only_keys:
|
|
535
|
+
self._reflect_only_elements = self._reflect_only_elements.union(
|
|
536
|
+
{key: value}
|
|
537
|
+
)
|
|
538
|
+
else:
|
|
539
|
+
self._non_defaults[key] = value
|
|
466
540
|
|
|
467
541
|
def __delitem__(self, key: str) -> None:
|
|
468
542
|
del self._non_defaults[key]
|
|
@@ -525,6 +599,11 @@ class DialectKWArgs:
|
|
|
525
599
|
return else_
|
|
526
600
|
|
|
527
601
|
if argument_name in registry.get(self.__class__, {}):
|
|
602
|
+
if (
|
|
603
|
+
argument_name
|
|
604
|
+
in self.dialect_options[dialect.name]._reflection_only_keys
|
|
605
|
+
):
|
|
606
|
+
return else_
|
|
528
607
|
if (
|
|
529
608
|
deprecated_fallback is None
|
|
530
609
|
or dialect.name == deprecated_fallback
|
|
@@ -646,6 +725,60 @@ class DialectKWArgs:
|
|
|
646
725
|
"""A synonym for :attr:`.DialectKWArgs.dialect_kwargs`."""
|
|
647
726
|
return self.dialect_kwargs
|
|
648
727
|
|
|
728
|
+
@property
|
|
729
|
+
def reflect_only_elements(self) -> Mapping[str, Mapping[str, Any]]:
|
|
730
|
+
"""A read-only collection of dialect-specific database state
|
|
731
|
+
reported by reflection, separate from DDL options.
|
|
732
|
+
|
|
733
|
+
Holds values for arguments whose
|
|
734
|
+
:attr:`.DefaultDialect.construct_arguments` default is
|
|
735
|
+
:attr:`.DialectKWArgConst.REFLECTED_ONLY`. Like
|
|
736
|
+
:attr:`.DialectKWArgs.dialect_options`, this is a two-level nested
|
|
737
|
+
collection keyed to ``<dialect_name>`` and ``<argument_name>``; a
|
|
738
|
+
dialect name with no values present returns an empty mapping, for
|
|
739
|
+
example::
|
|
740
|
+
|
|
741
|
+
invalid = my_index.reflect_only_elements["postgresql"].get("invalid")
|
|
742
|
+
|
|
743
|
+
These values are not included in
|
|
744
|
+
:attr:`.DialectKWArgs.dialect_options` or
|
|
745
|
+
:attr:`.DialectKWArgs.dialect_kwargs` and take no part in DDL
|
|
746
|
+
compilation. They are carried along to copies of the object, such
|
|
747
|
+
as those produced by :meth:`.Table.to_metadata`.
|
|
748
|
+
|
|
749
|
+
.. versionadded:: 2.1
|
|
750
|
+
|
|
751
|
+
.. seealso::
|
|
752
|
+
|
|
753
|
+
:attr:`.DialectKWArgConst.REFLECTED_ONLY`
|
|
754
|
+
|
|
755
|
+
""" # noqa: E501
|
|
756
|
+
return _ReflectOnlyView(self)
|
|
757
|
+
|
|
758
|
+
@property
|
|
759
|
+
def _reflect_only_kwargs(self) -> Dict[str, Any]:
|
|
760
|
+
"""The contents of :attr:`.DialectKWArgs.reflect_only_elements` in
|
|
761
|
+
flat ``<dialect>_<argument>`` form, suitable to be passed to the
|
|
762
|
+
constructor of a copy of this object."""
|
|
763
|
+
|
|
764
|
+
return {
|
|
765
|
+
f"{dialect_name}_{key}": value
|
|
766
|
+
for dialect_name, elements in self.reflect_only_elements.items()
|
|
767
|
+
for key, value in elements.items()
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
def _copy_reflect_only_elements(self, other: DialectKWArgs) -> None:
|
|
771
|
+
"""Copy the contents of :attr:`.DialectKWArgs.reflect_only_elements`
|
|
772
|
+
onto ``other``, a copy of this object."""
|
|
773
|
+
|
|
774
|
+
if "dialect_options" not in self.__dict__:
|
|
775
|
+
return
|
|
776
|
+
for dialect_name, args in self.dialect_options.items():
|
|
777
|
+
if args._reflect_only_elements:
|
|
778
|
+
other.dialect_options[dialect_name]._reflect_only_elements = (
|
|
779
|
+
args._reflect_only_elements
|
|
780
|
+
)
|
|
781
|
+
|
|
649
782
|
_kw_registry: util.PopulateDict[str, Optional[Dict[Any, Any]]] = (
|
|
650
783
|
util.PopulateDict(_kw_reg_for_dialect)
|
|
651
784
|
)
|
|
@@ -661,6 +794,13 @@ class DialectKWArgs:
|
|
|
661
794
|
for cls in reversed(cls.__mro__):
|
|
662
795
|
if cls in construct_arg_dictionary:
|
|
663
796
|
d._defaults.update(construct_arg_dictionary[cls])
|
|
797
|
+
d._reflection_only_keys = frozenset(
|
|
798
|
+
key
|
|
799
|
+
for key, value in d._defaults.items()
|
|
800
|
+
if value is DialectKWArgConst.REFLECTED_ONLY
|
|
801
|
+
)
|
|
802
|
+
for key in d._reflection_only_keys:
|
|
803
|
+
del d._defaults[key]
|
|
664
804
|
return d
|
|
665
805
|
|
|
666
806
|
@util.memoized_property
|
|
@@ -676,10 +816,18 @@ class DialectKWArgs:
|
|
|
676
816
|
|
|
677
817
|
.. versionadded:: 0.9.2
|
|
678
818
|
|
|
819
|
+
Arguments a dialect declares as
|
|
820
|
+
:attr:`.DialectKWArgConst.REFLECTED_ONLY` are not included in this
|
|
821
|
+
collection; they are available from
|
|
822
|
+
:attr:`.DialectKWArgs.reflect_only_elements`.
|
|
823
|
+
|
|
679
824
|
.. seealso::
|
|
680
825
|
|
|
681
826
|
:attr:`.DialectKWArgs.dialect_kwargs` - flat dictionary form
|
|
682
827
|
|
|
828
|
+
:attr:`.DialectKWArgs.reflect_only_elements` - database state
|
|
829
|
+
reported by reflection
|
|
830
|
+
|
|
683
831
|
"""
|
|
684
832
|
|
|
685
833
|
return util.PopulateDict(self._kw_reg_for_dialect_cls)
|
|
@@ -714,6 +862,8 @@ class DialectKWArgs:
|
|
|
714
862
|
if (
|
|
715
863
|
"*" not in construct_arg_dictionary
|
|
716
864
|
and arg_name not in construct_arg_dictionary
|
|
865
|
+
and arg_name
|
|
866
|
+
not in construct_arg_dictionary._reflection_only_keys
|
|
717
867
|
):
|
|
718
868
|
raise exc.ArgumentError(
|
|
719
869
|
"Argument %r is not accepted by "
|
sqlalchemy/sql/schema.py
CHANGED
|
@@ -265,6 +265,9 @@ class SchemaItem(SchemaVisitable):
|
|
|
265
265
|
def _schema_item_copy(self, schema_item: _SI) -> _SI:
|
|
266
266
|
if "info" in self.__dict__:
|
|
267
267
|
schema_item.info = self.info.copy()
|
|
268
|
+
if isinstance(self, DialectKWArgs):
|
|
269
|
+
assert isinstance(schema_item, DialectKWArgs)
|
|
270
|
+
self._copy_reflect_only_elements(schema_item)
|
|
268
271
|
schema_item.dispatch._update(self.dispatch)
|
|
269
272
|
return schema_item
|
|
270
273
|
|
|
@@ -1766,6 +1769,7 @@ class Table(
|
|
|
1766
1769
|
],
|
|
1767
1770
|
_table=table,
|
|
1768
1771
|
**index.kwargs,
|
|
1772
|
+
**index._reflect_only_kwargs,
|
|
1769
1773
|
)
|
|
1770
1774
|
return self._schema_item_copy(table)
|
|
1771
1775
|
|
|
@@ -4655,6 +4659,7 @@ class Sequence(HasSchemaAttr, IdentityOptions, DefaultGenerator):
|
|
|
4655
4659
|
for_update=self.for_update,
|
|
4656
4660
|
**self._as_dict(),
|
|
4657
4661
|
**self.dialect_kwargs,
|
|
4662
|
+
**self._reflect_only_kwargs,
|
|
4658
4663
|
)
|
|
4659
4664
|
|
|
4660
4665
|
def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
|
sqlalchemy/sql/selectable.py
CHANGED
|
@@ -1654,6 +1654,19 @@ class Join(roles.DMLTableRole, FromClause[_KeyColCC_co]):
|
|
|
1654
1654
|
"""
|
|
1655
1655
|
return Select(self.left, self.right).select_from(self)
|
|
1656
1656
|
|
|
1657
|
+
@staticmethod
|
|
1658
|
+
def _flat_element_name(
|
|
1659
|
+
name: Optional[str], element: FromClause
|
|
1660
|
+
) -> Optional[str]:
|
|
1661
|
+
if name and isinstance(element, NamedFromClause):
|
|
1662
|
+
if isinstance(element.name, _anonymous_label):
|
|
1663
|
+
# an anonymous name can't be embedded within a new name;
|
|
1664
|
+
# alias the element anonymously, #13583
|
|
1665
|
+
return None
|
|
1666
|
+
return f"{name}_{element.name}"
|
|
1667
|
+
else:
|
|
1668
|
+
return name
|
|
1669
|
+
|
|
1657
1670
|
@util.preload_module("sqlalchemy.sql.util")
|
|
1658
1671
|
def _anonymous_fromclause(
|
|
1659
1672
|
self, name: Optional[str] = None, flat: bool = False
|
|
@@ -1663,17 +1676,11 @@ class Join(roles.DMLTableRole, FromClause[_KeyColCC_co]):
|
|
|
1663
1676
|
if isinstance(self.left, (FromGrouping, Join)):
|
|
1664
1677
|
left_name = name # will recurse
|
|
1665
1678
|
else:
|
|
1666
|
-
|
|
1667
|
-
left_name = f"{name}_{self.left.name}"
|
|
1668
|
-
else:
|
|
1669
|
-
left_name = name
|
|
1679
|
+
left_name = self._flat_element_name(name, self.left)
|
|
1670
1680
|
if isinstance(self.right, (FromGrouping, Join)):
|
|
1671
1681
|
right_name = name # will recurse
|
|
1672
1682
|
else:
|
|
1673
|
-
|
|
1674
|
-
right_name = f"{name}_{self.right.name}"
|
|
1675
|
-
else:
|
|
1676
|
-
right_name = name
|
|
1683
|
+
right_name = self._flat_element_name(name, self.right)
|
|
1677
1684
|
left_a, right_a = (
|
|
1678
1685
|
self.left._anonymous_fromclause(name=left_name, flat=flat),
|
|
1679
1686
|
self.right._anonymous_fromclause(name=right_name, flat=flat),
|
|
@@ -2291,7 +2298,10 @@ class CTE(
|
|
|
2291
2298
|
name=name,
|
|
2292
2299
|
recursive=self.recursive,
|
|
2293
2300
|
nesting=self.nesting,
|
|
2294
|
-
|
|
2301
|
+
# an alias of an alias refers to the original CTE, #13583
|
|
2302
|
+
_cte_alias=(
|
|
2303
|
+
self._cte_alias if self._cte_alias is not None else self
|
|
2304
|
+
),
|
|
2295
2305
|
_prefixes=self._prefixes,
|
|
2296
2306
|
_suffixes=self._suffixes,
|
|
2297
2307
|
)
|
|
@@ -483,6 +483,19 @@ class SuiteRequirements(Requirements):
|
|
|
483
483
|
"%(database)s %(does_support)s 'UPDATE ... RETURNING'",
|
|
484
484
|
)
|
|
485
485
|
|
|
486
|
+
@property
|
|
487
|
+
def update_returning_dialect_not_supported(self):
|
|
488
|
+
"""target database rejects UPDATE ... RETURNING when the dialect
|
|
489
|
+
reports that ``update_returning`` is not supported.
|
|
490
|
+
|
|
491
|
+
This is normally the case; it's false only where a server has
|
|
492
|
+
gained UPDATE ... RETURNING that the dialect doesn't implement
|
|
493
|
+
yet, so that the statement runs rather than raising.
|
|
494
|
+
|
|
495
|
+
"""
|
|
496
|
+
|
|
497
|
+
return exclusions.open()
|
|
498
|
+
|
|
486
499
|
@property
|
|
487
500
|
def insert_executemany_returning(self):
|
|
488
501
|
"""target platform supports RETURNING when INSERT is used with
|
|
@@ -1698,6 +1711,12 @@ class SuiteRequirements(Requirements):
|
|
|
1698
1711
|
lambda: not util.freethreading, "GIL-enabled build needed"
|
|
1699
1712
|
)
|
|
1700
1713
|
|
|
1714
|
+
@property
|
|
1715
|
+
def freethreading(self):
|
|
1716
|
+
return exclusions.only_if(
|
|
1717
|
+
lambda: util.freethreading, "free-threaded build needed"
|
|
1718
|
+
)
|
|
1719
|
+
|
|
1701
1720
|
@property
|
|
1702
1721
|
def is64bit(self):
|
|
1703
1722
|
return exclusions.only_if(lambda: util.is64bit, "64bit required")
|
|
@@ -707,6 +707,7 @@ class ReturningGuardsTest(fixtures.TablesTest):
|
|
|
707
707
|
stmt, True, "id", connection.dialect.insert_executemany_returning
|
|
708
708
|
)
|
|
709
709
|
|
|
710
|
+
@testing.requires.update_returning_dialect_not_supported
|
|
710
711
|
def test_update_single(self, connection, run_stmt):
|
|
711
712
|
t = self.tables.t
|
|
712
713
|
|
|
@@ -100,6 +100,43 @@ class SimpleUpdateDeleteTest(fixtures.TablesTest):
|
|
|
100
100
|
),
|
|
101
101
|
)
|
|
102
102
|
|
|
103
|
+
@testing.variation("unchanged", ["some", "all"])
|
|
104
|
+
@testing.requires.update_returning
|
|
105
|
+
def test_update_returning_unchanged_rows(self, connection, unchanged):
|
|
106
|
+
"""test that UPDATE..RETURNING returns all rows matched by the WHERE
|
|
107
|
+
clause, including those where the new value is the same as the
|
|
108
|
+
existing value.
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
t = self.tables.plain_pk
|
|
112
|
+
|
|
113
|
+
if unchanged.some:
|
|
114
|
+
# WHERE matches 2, 1 row changed
|
|
115
|
+
stmt = t.update().where(t.c.id.in_([2, 3]))
|
|
116
|
+
elif unchanged.all:
|
|
117
|
+
# WHERE matches 1, 0 rows changed
|
|
118
|
+
stmt = t.update().where(t.c.id == 3)
|
|
119
|
+
else:
|
|
120
|
+
unchanged.fail()
|
|
121
|
+
|
|
122
|
+
r = connection.execute(
|
|
123
|
+
stmt.returning(t.c.id, t.c.data), dict(data="d3")
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if unchanged.some:
|
|
127
|
+
eq_(sorted(r.all()), [(2, "d3"), (3, "d3")])
|
|
128
|
+
else:
|
|
129
|
+
eq_(r.all(), [(3, "d3")])
|
|
130
|
+
|
|
131
|
+
eq_(
|
|
132
|
+
connection.execute(t.select().order_by(t.c.id)).fetchall(),
|
|
133
|
+
(
|
|
134
|
+
[(1, "d1"), (2, "d3"), (3, "d3")]
|
|
135
|
+
if unchanged.some
|
|
136
|
+
else [(1, "d1"), (2, "d2"), (3, "d3")]
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
|
|
103
140
|
@testing.variation("criteria", ["rows", "norows", "emptyin"])
|
|
104
141
|
@testing.requires.delete_returning
|
|
105
142
|
def test_delete_returning(self, connection, criteria):
|
sqlalchemy/util/langhelpers.py
CHANGED
|
@@ -404,7 +404,13 @@ def _remove_linecache_entry(filename: str, entry: _LinecacheEntry) -> None:
|
|
|
404
404
|
linecache within exec_code_in_env.
|
|
405
405
|
|
|
406
406
|
"""
|
|
407
|
-
|
|
407
|
+
try:
|
|
408
|
+
linecache_cache = _linecache_cache_getter()
|
|
409
|
+
except AttributeError:
|
|
410
|
+
# linecache.cache is gone or is no longer a dict; as this runs
|
|
411
|
+
# from a finalizer, an exception here would only surface as an
|
|
412
|
+
# unraisable error
|
|
413
|
+
return
|
|
408
414
|
|
|
409
415
|
if linecache_cache.get(filename) is entry:
|
|
410
416
|
linecache_cache.pop(filename, None)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SQLAlchemy
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Database Abstraction Library
|
|
5
5
|
Author-email: Mike Bayer <mike_mp@zzzcomputing.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -86,28 +86,6 @@ Requires-Dist: sqlalchemy[asyncio]; extra == "aiosqlite"
|
|
|
86
86
|
Requires-Dist: aiosqlite; extra == "aiosqlite"
|
|
87
87
|
Provides-Extra: sqlcipher
|
|
88
88
|
Requires-Dist: sqlcipher3_binary; extra == "sqlcipher"
|
|
89
|
-
Provides-Extra: mssql-pymssql
|
|
90
|
-
Requires-Dist: sqlalchemy[mssql-pymssql]; extra == "mssql-pymssql"
|
|
91
|
-
Provides-Extra: mssql-pyodbc
|
|
92
|
-
Requires-Dist: sqlalchemy[mssql-pyodbc]; extra == "mssql-pyodbc"
|
|
93
|
-
Provides-Extra: mysql-connector
|
|
94
|
-
Requires-Dist: sqlalchemy[mysql-connector]; extra == "mysql-connector"
|
|
95
|
-
Provides-Extra: mariadb-connector
|
|
96
|
-
Requires-Dist: sqlalchemy[mariadb-connector]; extra == "mariadb-connector"
|
|
97
|
-
Provides-Extra: oracle-oracledb
|
|
98
|
-
Requires-Dist: sqlalchemy[oracle-oracledb]; extra == "oracle-oracledb"
|
|
99
|
-
Provides-Extra: postgresql-pg8000
|
|
100
|
-
Requires-Dist: sqlalchemy[postgresql-pg8000]; extra == "postgresql-pg8000"
|
|
101
|
-
Provides-Extra: postgresql-asyncpg
|
|
102
|
-
Requires-Dist: sqlalchemy[postgresql-asyncpg]; extra == "postgresql-asyncpg"
|
|
103
|
-
Provides-Extra: postgresql-psycopg2binary
|
|
104
|
-
Requires-Dist: sqlalchemy[postgresql-psycopg2binary]; extra == "postgresql-psycopg2binary"
|
|
105
|
-
Provides-Extra: postgresql-psycopg2cffi
|
|
106
|
-
Requires-Dist: sqlalchemy[postgresql-psycopg2cffi]; extra == "postgresql-psycopg2cffi"
|
|
107
|
-
Provides-Extra: postgresql-psycopg
|
|
108
|
-
Requires-Dist: sqlalchemy[postgresql-psycopg]; extra == "postgresql-psycopg"
|
|
109
|
-
Provides-Extra: postgresql-psycopgbinary
|
|
110
|
-
Requires-Dist: sqlalchemy[postgresql-psycopgbinary]; extra == "postgresql-psycopgbinary"
|
|
111
89
|
Dynamic: license-file
|
|
112
90
|
|
|
113
91
|
SQLAlchemy
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
sqlalchemy/__init__.py,sha256=
|
|
1
|
+
sqlalchemy/__init__.py,sha256=ujjm1qYHLGW9VikKFzIo5e1rkEv3dyvpvw9r8X2htw8,13562
|
|
2
2
|
sqlalchemy/events.py,sha256=vEDW04X_PMhMw5y0_95tbEzht3wua8_gnkMBJstYNRw,525
|
|
3
3
|
sqlalchemy/exc.py,sha256=CKyzx9WSeWTYxueZLAtTFpZTNKSon0YlpGt9v-C8dl8,27503
|
|
4
4
|
sqlalchemy/inspection.py,sha256=2mcgybcOVuQDB2bSIhwidrMteP9qOpEbmehyT2lrnt8,5661
|
|
@@ -18,7 +18,7 @@ sqlalchemy/dialects/mssql/aioodbc.py,sha256=RS9TZI4cDa9My9YHQbWW2nD3lUBx-s8aQZ7G
|
|
|
18
18
|
sqlalchemy/dialects/mssql/base.py,sha256=w_R8bOhMkDa6qRQEyqbcRepL9RF2ZaP8jmovMtJ7Kac,159925
|
|
19
19
|
sqlalchemy/dialects/mssql/information_schema.py,sha256=xoO2mKqq99GBN9osJaaYjLw3VQg4YB8rFrh5K6gGfaI,10544
|
|
20
20
|
sqlalchemy/dialects/mssql/json.py,sha256=SgcLWK-xmy_5FCzgxoz2Xa8pnJemVFCuiNQ6aFApHyk,5338
|
|
21
|
-
sqlalchemy/dialects/mssql/mssqlpython.py,sha256=
|
|
21
|
+
sqlalchemy/dialects/mssql/mssqlpython.py,sha256=Boe6dsrabesUauvFUDIHeFXDXLBh3qi2pgqYCqI__eI,7832
|
|
22
22
|
sqlalchemy/dialects/mssql/provision.py,sha256=KUTOtIMDDSi9R3uoIkTJiz_L3qUg6rFvDY6RzbgQsl4,6884
|
|
23
23
|
sqlalchemy/dialects/mssql/pymssql.py,sha256=gIeA411WXjMKBg_Zg8pPc4alci3OqF9yTzVqwUaRCEY,4215
|
|
24
24
|
sqlalchemy/dialects/mssql/pyodbc.py,sha256=hcyGiZb1dJUsegbwdYRjotr9UPvPZkXx9Bt-oorN8hc,25100
|
|
@@ -47,7 +47,7 @@ sqlalchemy/dialects/oracle/base.py,sha256=TDl2u3tL6QzbDHTbrmnltQhxgqOvrG9lVdLzV4
|
|
|
47
47
|
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=ITGIIUnkWFLm9jb4Z7VD1UrZGebVDORrvFY_ZzLPOqM,63932
|
|
48
48
|
sqlalchemy/dialects/oracle/dictionary.py,sha256=hotIZctkY58oepLJ3NYQxWb44oMOurxsXgD2yhe3neg,19519
|
|
49
49
|
sqlalchemy/dialects/oracle/json.py,sha256=JingOTIOeoiN9ymSFK7K5RnUefKzEgQzb5RlTNR8hfY,6218
|
|
50
|
-
sqlalchemy/dialects/oracle/oracledb.py,sha256=
|
|
50
|
+
sqlalchemy/dialects/oracle/oracledb.py,sha256=uFfhyZazx2NcIwiTxeSy570LbJP6MV_GDjH0qAzP0Vk,32944
|
|
51
51
|
sqlalchemy/dialects/oracle/provision.py,sha256=SiDKY7lVY5DsEMI1HEsoQe1cvzSL6AdrrliQtRonLgc,10685
|
|
52
52
|
sqlalchemy/dialects/oracle/types.py,sha256=iVayov9nyK1MCIS26BvawokAn4PwIXIvdgH3j-JB56E,10894
|
|
53
53
|
sqlalchemy/dialects/oracle/vector.py,sha256=v7zf88XX7ICORNuycw3da1d5G7KHmYytNtU6pI9Mv-4,10922
|
|
@@ -55,7 +55,7 @@ sqlalchemy/dialects/postgresql/__init__.py,sha256=uqvgSydDm7mbLeLtuARI8AXFkcHPCk
|
|
|
55
55
|
sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=3btoOFqAcFmkectNE1Vgs7cOv4ccF28xIQZIDEYwOWA,7178
|
|
56
56
|
sqlalchemy/dialects/postgresql/array.py,sha256=fHA_e2ez1SGHXa4BrXxbwCKMEkrDxn2GFzWJOjZrM0c,17907
|
|
57
57
|
sqlalchemy/dialects/postgresql/asyncpg.py,sha256=IE2FNLURUBGvsofgGpqCJ6sx8Qd_alUsOz2Mb0YkeVk,42376
|
|
58
|
-
sqlalchemy/dialects/postgresql/base.py,sha256=
|
|
58
|
+
sqlalchemy/dialects/postgresql/base.py,sha256=lxkSsXXsKcEGbQwGXVrF7lY2Vn5_iEetJ3T7gxrrYk0,213886
|
|
59
59
|
sqlalchemy/dialects/postgresql/bitstring.py,sha256=-AowJ4DqlcT0PcFXML44TU4VND_hFuvtkwqUgKBcE1I,10603
|
|
60
60
|
sqlalchemy/dialects/postgresql/dml.py,sha256=ckxPBWuAXCRKkSFvdmwEppvirvczFY-voniqgC3X6DA,12959
|
|
61
61
|
sqlalchemy/dialects/postgresql/ext.py,sha256=ZgdhLnWDYcm3RQ0XlUtrsW3WF1YRcGmzEK-8sdg16wc,19603
|
|
@@ -90,7 +90,7 @@ sqlalchemy/engine/create.py,sha256=7G0aidSUYtghZjyv7C19TZcv_3o3pS62UXj-sRgsWVs,3
|
|
|
90
90
|
sqlalchemy/engine/cursor.py,sha256=B5NmeqIEsOiPU8QaE4dXh7s1cSp2_5erukysuKs7ipo,85474
|
|
91
91
|
sqlalchemy/engine/default.py,sha256=nwcP1u060d0UpKWUnpTqZNyeTX_5zsbJnEyS9v_M5Wg,94262
|
|
92
92
|
sqlalchemy/engine/events.py,sha256=d-BzEd6M8dG8q22w3N6RX98cDcrANTgYyPjb5oFQznE,37475
|
|
93
|
-
sqlalchemy/engine/interfaces.py,sha256=
|
|
93
|
+
sqlalchemy/engine/interfaces.py,sha256=JU4s2sK_LPKYM9Bqegb_eoAdUbTHO0-zSTN_sdtHwCU,122305
|
|
94
94
|
sqlalchemy/engine/mock.py,sha256=i9J-z0NsL78VlqC85MRgo5SNNQU256UtEqficO3DCwY,4155
|
|
95
95
|
sqlalchemy/engine/processors.py,sha256=uh6xb1ajvSzJufQDG2DmmMeP0VbgaeSHZOJPBSN3mwU,2710
|
|
96
96
|
sqlalchemy/engine/reflection.py,sha256=vbRdfQ00pekzzdC3mwL6gwZ8iFnsVIbiawvDzoXTXBc,77136
|
|
@@ -129,7 +129,7 @@ sqlalchemy/ext/declarative/extensions.py,sha256=xcTpvgQo_DetyXNZtvnSZRaNTczVYIUh
|
|
|
129
129
|
sqlalchemy/future/__init__.py,sha256=9pngQPAhogCThkP0WMqp43MYiMHQ8usQekk0IBhrUYw,513
|
|
130
130
|
sqlalchemy/future/engine.py,sha256=CNIeZz-03GSBylYCTTam_Yk5kHD0EprfTxaNEgQc-0I,495
|
|
131
131
|
sqlalchemy/orm/__init__.py,sha256=VhzsKd_oKslCrurAbQii-0IJ4wuDngQnLwnfZhb80GI,8791
|
|
132
|
-
sqlalchemy/orm/_orm_constructors.py,sha256=
|
|
132
|
+
sqlalchemy/orm/_orm_constructors.py,sha256=73M6QqtNY8H2NgwMfUjooo8eoPP8DvyIC9wgCiWO44g,107879
|
|
133
133
|
sqlalchemy/orm/_typing.py,sha256=dj7GE9yxelGlP5XeJXAH3VHNawd6eBxcJyoQht0tVS8,5012
|
|
134
134
|
sqlalchemy/orm/attributes.py,sha256=SJLVDunnq00povV2vfZq_b7c9wTWv0PSaV7W7Gx9YmM,93774
|
|
135
135
|
sqlalchemy/orm/base.py,sha256=W8nHDIrPiFsRwiePCiSPRYLepUgM_gTsFi2UgvuFyeI,28350
|
|
@@ -140,7 +140,7 @@ sqlalchemy/orm/context.py,sha256=F1poIa-NftQTO_cNUfSnbJKjbPQs9kWy6tBEyznTD_8,120
|
|
|
140
140
|
sqlalchemy/orm/decl_api.py,sha256=gK5IGcbAXMdCN6SzprewMhfbXiZe1yO1Ghv2y2z3EHA,78721
|
|
141
141
|
sqlalchemy/orm/decl_base.py,sha256=IQzfu1cySobZg0-Boy3HQs11rO-hYrU1D6q6ERIXOPc,87150
|
|
142
142
|
sqlalchemy/orm/dependency.py,sha256=gqUUJi7LMF2RIEvq7hw_w-WfbpW8CXzSaDW5QTW5NlQ,47739
|
|
143
|
-
sqlalchemy/orm/descriptor_props.py,sha256=
|
|
143
|
+
sqlalchemy/orm/descriptor_props.py,sha256=2R4NUuPIyinL5RrTbOAYebb3ZJNJLnT6_J2QCWy0nEM,42407
|
|
144
144
|
sqlalchemy/orm/dynamic.py,sha256=7wyr0eM7xIRqWQk6jcoHjqGYatORwImRbrysJhQaGm8,9968
|
|
145
145
|
sqlalchemy/orm/evaluator.py,sha256=DkKS71DYkzZRtxXRh-8pCyup_X7Xfbi9FjWHceTicOA,12352
|
|
146
146
|
sqlalchemy/orm/events.py,sha256=fiDZRyN3-ikjkQEOypuADlkc_Jb9NXZEdd_wIAiikrI,131318
|
|
@@ -160,11 +160,11 @@ sqlalchemy/orm/scoping.py,sha256=eGXEv3sthVrBau1t7XCy-Irq9DA1uc7vpqw0IulAwjk,811
|
|
|
160
160
|
sqlalchemy/orm/session.py,sha256=6-nextPWqVjwA7hW79oozm6pXbGHXF7Js3HLfJcgyUQ,203522
|
|
161
161
|
sqlalchemy/orm/state.py,sha256=IxVE17w39HuK_U3vYKEGry-TE53ptfdCVuZmHjKn2MM,38600
|
|
162
162
|
sqlalchemy/orm/state_changes.py,sha256=zbtHp9OaZhU80uTQSPyiDyQuCOQZsYil4mdiRa0Lvrw,6809
|
|
163
|
-
sqlalchemy/orm/strategies.py,sha256=
|
|
163
|
+
sqlalchemy/orm/strategies.py,sha256=PSEGq2fvV8YECRp03L5LSlsIMMFgoUGug-quSU0ksPQ,124396
|
|
164
164
|
sqlalchemy/orm/strategy_options.py,sha256=Wzfd8hB-Xqd5eubYNfIfcsjzdd9DOFpsI0V4cvHUZ6k,89057
|
|
165
165
|
sqlalchemy/orm/sync.py,sha256=j1v5g0M_0_cSKiv6UEek-DhPWAL9eSpc4hY9Z14c988,5785
|
|
166
166
|
sqlalchemy/orm/unitofwork.py,sha256=ewxWWxVZw62LndJL0K_L3VFaACe_9m7ub0w-j2vyIjU,27135
|
|
167
|
-
sqlalchemy/orm/util.py,sha256=
|
|
167
|
+
sqlalchemy/orm/util.py,sha256=hyHPD38nPxcJ7OpoJMMHW4kG4hWx-xj9OXTtY2D-gDs,86884
|
|
168
168
|
sqlalchemy/orm/writeonly.py,sha256=LAbF6qIkV6agnH6f9piLFdsPvdewLsvhEagNebd-Kig,23096
|
|
169
169
|
sqlalchemy/pool/__init__.py,sha256=h3EJgaBrR5w7aTqbf9cH8j3qf64jKj4fGe7nQ61jKNg,1714
|
|
170
170
|
sqlalchemy/pool/base.py,sha256=ZRhJfpRiCBE8yqlrDPY5_NgiASxZ711FSHM3T6NtRLA,53716
|
|
@@ -181,7 +181,7 @@ sqlalchemy/sql/_typing.py,sha256=jDHln_-yp5AqbFY_B1uyFwuWAxrYF_iTJZ-KAb54tgI,140
|
|
|
181
181
|
sqlalchemy/sql/_util_cy.pxd,sha256=JEkdMfO03ulZoZSalWA0goLog-TxIfEuXkZZadW0ZGg,349
|
|
182
182
|
sqlalchemy/sql/_util_cy.py,sha256=vluLhp88b-vWkXIuZb971L9oRao7mlMDHk-DUGKGgIA,3871
|
|
183
183
|
sqlalchemy/sql/annotation.py,sha256=gkFPOq3qGo6fahIxTwXgxCdcEyiD8W4CH0q01d1E0mo,18487
|
|
184
|
-
sqlalchemy/sql/base.py,sha256=
|
|
184
|
+
sqlalchemy/sql/base.py,sha256=1xkh7mgC25GVZS8tczZbwDAxRvdumoaW3UPrJXbynbQ,98615
|
|
185
185
|
sqlalchemy/sql/cache_key.py,sha256=OihJqOVsdU0F_IShr0FIptTRHtIlM1UIZ_5LR8LQd0Q,28872
|
|
186
186
|
sqlalchemy/sql/coercions.py,sha256=XyV6wyiIHHD6ViGdZhLMQ8OVWNk4_LyBvv15t504UYI,39144
|
|
187
187
|
sqlalchemy/sql/compiler.py,sha256=7xY9nqfs7_svpZqFNjxF4KOh2sVJtBE0ws1_XuQTlv0,299616
|
|
@@ -197,8 +197,8 @@ sqlalchemy/sql/lambdas.py,sha256=msXSQmXp2sVWDalooJBcm5xKXdLjSlf5vyNX8vdOq3A,494
|
|
|
197
197
|
sqlalchemy/sql/naming.py,sha256=qBLfM2BizVlnFrDTcZ2AzTiUMml281BiOHRD-Qw1uXg,6705
|
|
198
198
|
sqlalchemy/sql/operators.py,sha256=91HJboxRkDFNHx9JlpO9hsss7wqTfeoANOxjMFszTt0,87215
|
|
199
199
|
sqlalchemy/sql/roles.py,sha256=QG4iNsYMCqjnMP3wt-5lEKxQZIh3uD2yVhurcS-z7VM,7857
|
|
200
|
-
sqlalchemy/sql/schema.py,sha256=
|
|
201
|
-
sqlalchemy/sql/selectable.py,sha256=
|
|
200
|
+
sqlalchemy/sql/schema.py,sha256=55oc5r8Mqtkcr2ker8kHjlBSUSO3QhwyyqrG2gaFgu4,262469
|
|
201
|
+
sqlalchemy/sql/selectable.py,sha256=xadnLUCBQFChbp2xMGjZrwXz86iOpUPBMGF8aPxYcgY,257659
|
|
202
202
|
sqlalchemy/sql/sqltypes.py,sha256=Qa9Sl6iuLx0pB73O0LNgr_fauY8rGl_f9dU5wbOwth8,138409
|
|
203
203
|
sqlalchemy/sql/traversals.py,sha256=xLBzv1qpTUjBTiTGMBvPUvl1UPffYSWMxV4wHgOM4i4,34355
|
|
204
204
|
sqlalchemy/sql/type_api.py,sha256=ZWQkgF2vH61G4qNfv2cREPOUXeM5o0QaOHWkb1cmILo,88622
|
|
@@ -217,7 +217,7 @@ sqlalchemy/testing/pickleable.py,sha256=gXgySNI39dUcuNY6e-vM9HFKxQ7Uva7NVZgeYyNY
|
|
|
217
217
|
sqlalchemy/testing/profiles_file.py,sha256=sxwF-c6Wd6IBEMHpU1QSdsouyCX7b-4EWWsTTdD8m4w,11349
|
|
218
218
|
sqlalchemy/testing/profiling.py,sha256=pCeurQNGnsyMHotKuaT8GM8Aq_p1JADaIXPsWsvEJ9w,9159
|
|
219
219
|
sqlalchemy/testing/provision.py,sha256=pOMK51WaXJyWAh48HbBjgywDPLk02pSS7rHQqoMLulI,18628
|
|
220
|
-
sqlalchemy/testing/requirements.py,sha256=
|
|
220
|
+
sqlalchemy/testing/requirements.py,sha256=gX6WD4YhhOSF1f8Hmc3Fl0odNQKqXlJuAxO4PK1G1c0,57705
|
|
221
221
|
sqlalchemy/testing/schema.py,sha256=49x-TXARL2k8oIiaSzyGxXG5fh7vJOuBoUv5h9YTfKo,5360
|
|
222
222
|
sqlalchemy/testing/util.py,sha256=vbd6v3shfmdxqnqbL0K_hg-yi-ou5Fe9FpkUZeCEyUk,15809
|
|
223
223
|
sqlalchemy/testing/warnings.py,sha256=j8GWiP-yraCW0Ig5iJxAa5ekN82ZWqcQWyR-BUb4MZA,1546
|
|
@@ -233,7 +233,7 @@ sqlalchemy/testing/plugin/pytestplugin.py,sha256=swDvlVLofRYz3Q-w-svvHgdWIPoS_bv
|
|
|
233
233
|
sqlalchemy/testing/suite/__init__.py,sha256=jLYLmFQ6k0Fn7N2iPrbXwzJMKDSe4oMdmD-AC6yu3ws,726
|
|
234
234
|
sqlalchemy/testing/suite/test_cte.py,sha256=7AnV9dR9h76JWY0yWuyeQT-5GQsYbNMz3q2SG8IA_R4,7276
|
|
235
235
|
sqlalchemy/testing/suite/test_ddl.py,sha256=03nV2s8lfcCw4Czq6pkHl0ZGho8KyFe_C-f0HthP3B0,13010
|
|
236
|
-
sqlalchemy/testing/suite/test_dialect.py,sha256=
|
|
236
|
+
sqlalchemy/testing/suite/test_dialect.py,sha256=D72O2WP7iinte5psWDaJXO4uRyKZNVRTK4FaxxO9fy0,24276
|
|
237
237
|
sqlalchemy/testing/suite/test_insert.py,sha256=tapEYecSPKqL8xrjlcQzDOfbWdKJz9ldT7pRi0biDeM,18824
|
|
238
238
|
sqlalchemy/testing/suite/test_reflection.py,sha256=Em0XdladxD6Lk9YH6ZQUw0cdEvkaXJfEPs-1E21xVn0,128866
|
|
239
239
|
sqlalchemy/testing/suite/test_results.py,sha256=Aw8RK5CX1y39wDK6U8ub8PX9OYe8aoBCBCohjVPvRks,22499
|
|
@@ -243,7 +243,7 @@ sqlalchemy/testing/suite/test_sequence.py,sha256=8a6UO3pjRFnQ23ELG9m5cMMRoD3iD7a
|
|
|
243
243
|
sqlalchemy/testing/suite/test_table_via_select.py,sha256=ZMJKUhAiqIzTKyK9J2cA_rvTij7CUt77d6o5sqDdrkc,21706
|
|
244
244
|
sqlalchemy/testing/suite/test_types.py,sha256=zhL-us3ReUkX_klkbjYz0ve8op2ODSRTVLDo7OgWy38,72577
|
|
245
245
|
sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=tVLf9RqCJe-CsyhCWL9d-WuWazDx5iWYzoBrSCbgjXs,6141
|
|
246
|
-
sqlalchemy/testing/suite/test_update_delete.py,sha256=
|
|
246
|
+
sqlalchemy/testing/suite/test_update_delete.py,sha256=llAnxDGvbK-C7CzEXbSNEjczhOSL1sKTLuzGyJahqV8,5195
|
|
247
247
|
sqlalchemy/util/__init__.py,sha256=9e3x2jLp1offzq2pWK5xIHLVQXkQtBIGDmqP9ePNS_4,8621
|
|
248
248
|
sqlalchemy/util/_collections.py,sha256=kYBmJwkf3SSOmVyAPvRULLOM_3JmfXVWnxxEkEkgrUw,19157
|
|
249
249
|
sqlalchemy/util/_collections_cy.pxd,sha256=DjGW5D9N6Dv_wXdIscNTGKttmjcJBCSdz5lTHtvbe2o,291
|
|
@@ -254,15 +254,15 @@ sqlalchemy/util/compat.py,sha256=9_xMEtXJsbe5IWgKbqRT9u6bdgmq47c_xXc4FtFmxcc,891
|
|
|
254
254
|
sqlalchemy/util/concurrency.py,sha256=MpIMr-DqUnsYool27DfOeIMXndyzdWe_JnsktAYxLSc,8849
|
|
255
255
|
sqlalchemy/util/cython.py,sha256=mfWXvmFqP4y3eiA2kGgcgV5GR5sTZfOob32UGY1BYA4,1968
|
|
256
256
|
sqlalchemy/util/deprecations.py,sha256=9X3kYnZcVCEf1hyWU14_RNFahgUejlaLQX78B4UbhP8,12041
|
|
257
|
-
sqlalchemy/util/langhelpers.py,sha256=
|
|
257
|
+
sqlalchemy/util/langhelpers.py,sha256=HldAht7neroRJLZhgTFmw8wgmzVMYNIkKw7OkcwD9hw,84574
|
|
258
258
|
sqlalchemy/util/preloaded.py,sha256=DMfxtGeRyWpnUN3T9NvFdkPkTBPmruWOnUjLqrw8L0M,5975
|
|
259
259
|
sqlalchemy/util/queue.py,sha256=9cMV3Dea0l5tfWIHw3LUE0L3knt48gtUP_C8fBKLQeo,9740
|
|
260
260
|
sqlalchemy/util/tool_support.py,sha256=iZSwvKdF5pA8UtXuwbtln6dEDgerKJWasELSi7q6Epw,6136
|
|
261
261
|
sqlalchemy/util/topological.py,sha256=yMpaA2s0JYIekQF4XKwzJzy8lVo-8yeo8o1J2xzxq48,3451
|
|
262
262
|
sqlalchemy/util/typing.py,sha256=p3vOTJ4F7i3eqeFw7W_tayq88U0e53ZgOsCu7eNQ3lA,21125
|
|
263
|
-
sqlalchemy-2.1.
|
|
264
|
-
sqlalchemy-2.1.
|
|
265
|
-
sqlalchemy-2.1.
|
|
266
|
-
sqlalchemy-2.1.
|
|
267
|
-
sqlalchemy-2.1.
|
|
268
|
-
sqlalchemy-2.1.
|
|
263
|
+
sqlalchemy-2.1.1.dist-info/licenses/AUTHORS,sha256=3B2wtdF0Va3De2k_-OQJyagPYD5EFpWCVRjDb3hslj4,492
|
|
264
|
+
sqlalchemy-2.1.1.dist-info/licenses/LICENSE,sha256=6GK7W5BPtVE8PhQojXoloS6w7MeEegqWXTEOmpVefLk,1100
|
|
265
|
+
sqlalchemy-2.1.1.dist-info/METADATA,sha256=gdC5VZ7rLaHkNA6O7U9JtIPm8CICTuMa-YwGJkLM5u0,9680
|
|
266
|
+
sqlalchemy-2.1.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
267
|
+
sqlalchemy-2.1.1.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
|
|
268
|
+
sqlalchemy-2.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|