SQLAlchemy 2.0.47__cp313-cp313t-win_amd64.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 +283 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +184 -0
- sqlalchemy/connectors/asyncio.py +429 -0
- sqlalchemy/connectors/pyodbc.py +250 -0
- sqlalchemy/cyextension/__init__.py +6 -0
- sqlalchemy/cyextension/collections.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/collections.pyx +409 -0
- sqlalchemy/cyextension/immutabledict.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/immutabledict.pxd +8 -0
- sqlalchemy/cyextension/immutabledict.pyx +133 -0
- sqlalchemy/cyextension/processors.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/processors.pyx +68 -0
- sqlalchemy/cyextension/resultproxy.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/resultproxy.pyx +102 -0
- sqlalchemy/cyextension/util.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/util.pyx +90 -0
- sqlalchemy/dialects/__init__.py +62 -0
- sqlalchemy/dialects/_typing.py +30 -0
- sqlalchemy/dialects/mssql/__init__.py +88 -0
- sqlalchemy/dialects/mssql/aioodbc.py +63 -0
- sqlalchemy/dialects/mssql/base.py +4093 -0
- sqlalchemy/dialects/mssql/information_schema.py +285 -0
- sqlalchemy/dialects/mssql/json.py +129 -0
- sqlalchemy/dialects/mssql/provision.py +185 -0
- sqlalchemy/dialects/mssql/pymssql.py +126 -0
- sqlalchemy/dialects/mssql/pyodbc.py +760 -0
- sqlalchemy/dialects/mysql/__init__.py +104 -0
- sqlalchemy/dialects/mysql/aiomysql.py +250 -0
- sqlalchemy/dialects/mysql/asyncmy.py +231 -0
- sqlalchemy/dialects/mysql/base.py +3949 -0
- sqlalchemy/dialects/mysql/cymysql.py +106 -0
- sqlalchemy/dialects/mysql/dml.py +225 -0
- sqlalchemy/dialects/mysql/enumerated.py +282 -0
- sqlalchemy/dialects/mysql/expression.py +146 -0
- sqlalchemy/dialects/mysql/json.py +91 -0
- sqlalchemy/dialects/mysql/mariadb.py +72 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
- sqlalchemy/dialects/mysql/mysqldb.py +314 -0
- sqlalchemy/dialects/mysql/provision.py +153 -0
- sqlalchemy/dialects/mysql/pymysql.py +158 -0
- sqlalchemy/dialects/mysql/pyodbc.py +157 -0
- sqlalchemy/dialects/mysql/reflection.py +727 -0
- sqlalchemy/dialects/mysql/reserved_words.py +570 -0
- sqlalchemy/dialects/mysql/types.py +835 -0
- sqlalchemy/dialects/oracle/__init__.py +81 -0
- sqlalchemy/dialects/oracle/base.py +3802 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/oracledb.py +941 -0
- sqlalchemy/dialects/oracle/provision.py +297 -0
- sqlalchemy/dialects/oracle/types.py +316 -0
- sqlalchemy/dialects/oracle/vector.py +365 -0
- sqlalchemy/dialects/postgresql/__init__.py +167 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
- sqlalchemy/dialects/postgresql/array.py +519 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
- sqlalchemy/dialects/postgresql/base.py +5378 -0
- sqlalchemy/dialects/postgresql/dml.py +339 -0
- sqlalchemy/dialects/postgresql/ext.py +540 -0
- sqlalchemy/dialects/postgresql/hstore.py +406 -0
- sqlalchemy/dialects/postgresql/json.py +404 -0
- sqlalchemy/dialects/postgresql/named_types.py +524 -0
- sqlalchemy/dialects/postgresql/operators.py +129 -0
- sqlalchemy/dialects/postgresql/pg8000.py +669 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
- sqlalchemy/dialects/postgresql/provision.py +183 -0
- sqlalchemy/dialects/postgresql/psycopg.py +862 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
- sqlalchemy/dialects/postgresql/ranges.py +1031 -0
- sqlalchemy/dialects/postgresql/types.py +313 -0
- sqlalchemy/dialects/sqlite/__init__.py +57 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
- sqlalchemy/dialects/sqlite/base.py +3056 -0
- sqlalchemy/dialects/sqlite/dml.py +263 -0
- sqlalchemy/dialects/sqlite/json.py +92 -0
- sqlalchemy/dialects/sqlite/provision.py +229 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_py_processors.py +136 -0
- sqlalchemy/engine/_py_row.py +128 -0
- sqlalchemy/engine/_py_util.py +74 -0
- sqlalchemy/engine/base.py +3390 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +893 -0
- sqlalchemy/engine/cursor.py +2298 -0
- sqlalchemy/engine/default.py +2394 -0
- sqlalchemy/engine/events.py +965 -0
- sqlalchemy/engine/interfaces.py +3471 -0
- sqlalchemy/engine/mock.py +134 -0
- sqlalchemy/engine/processors.py +61 -0
- sqlalchemy/engine/reflection.py +2102 -0
- sqlalchemy/engine/result.py +2399 -0
- sqlalchemy/engine/row.py +400 -0
- sqlalchemy/engine/strategies.py +16 -0
- sqlalchemy/engine/url.py +924 -0
- sqlalchemy/engine/util.py +167 -0
- sqlalchemy/event/__init__.py +26 -0
- sqlalchemy/event/api.py +220 -0
- sqlalchemy/event/attr.py +676 -0
- sqlalchemy/event/base.py +472 -0
- sqlalchemy/event/legacy.py +258 -0
- sqlalchemy/event/registry.py +390 -0
- sqlalchemy/events.py +17 -0
- sqlalchemy/exc.py +832 -0
- sqlalchemy/ext/__init__.py +11 -0
- sqlalchemy/ext/associationproxy.py +2027 -0
- sqlalchemy/ext/asyncio/__init__.py +25 -0
- sqlalchemy/ext/asyncio/base.py +281 -0
- sqlalchemy/ext/asyncio/engine.py +1471 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +965 -0
- sqlalchemy/ext/asyncio/scoping.py +1599 -0
- sqlalchemy/ext/asyncio/session.py +1947 -0
- sqlalchemy/ext/automap.py +1701 -0
- sqlalchemy/ext/baked.py +570 -0
- sqlalchemy/ext/compiler.py +600 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +564 -0
- sqlalchemy/ext/horizontal_shard.py +478 -0
- sqlalchemy/ext/hybrid.py +1535 -0
- sqlalchemy/ext/indexable.py +364 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1085 -0
- sqlalchemy/ext/mypy/__init__.py +6 -0
- sqlalchemy/ext/mypy/apply.py +324 -0
- sqlalchemy/ext/mypy/decl_class.py +515 -0
- sqlalchemy/ext/mypy/infer.py +590 -0
- sqlalchemy/ext/mypy/names.py +335 -0
- sqlalchemy/ext/mypy/plugin.py +303 -0
- sqlalchemy/ext/mypy/util.py +357 -0
- sqlalchemy/ext/orderinglist.py +439 -0
- sqlalchemy/ext/serializer.py +185 -0
- sqlalchemy/future/__init__.py +16 -0
- sqlalchemy/future/engine.py +15 -0
- sqlalchemy/inspection.py +174 -0
- sqlalchemy/log.py +288 -0
- sqlalchemy/orm/__init__.py +171 -0
- sqlalchemy/orm/_orm_constructors.py +2661 -0
- sqlalchemy/orm/_typing.py +179 -0
- sqlalchemy/orm/attributes.py +2845 -0
- sqlalchemy/orm/base.py +971 -0
- sqlalchemy/orm/bulk_persistence.py +2135 -0
- sqlalchemy/orm/clsregistry.py +571 -0
- sqlalchemy/orm/collections.py +1627 -0
- sqlalchemy/orm/context.py +3334 -0
- sqlalchemy/orm/decl_api.py +2004 -0
- sqlalchemy/orm/decl_base.py +2192 -0
- sqlalchemy/orm/dependency.py +1302 -0
- sqlalchemy/orm/descriptor_props.py +1092 -0
- sqlalchemy/orm/dynamic.py +300 -0
- sqlalchemy/orm/evaluator.py +379 -0
- sqlalchemy/orm/events.py +3252 -0
- sqlalchemy/orm/exc.py +237 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +754 -0
- sqlalchemy/orm/interfaces.py +1496 -0
- sqlalchemy/orm/loading.py +1686 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4444 -0
- sqlalchemy/orm/path_registry.py +809 -0
- sqlalchemy/orm/persistence.py +1788 -0
- sqlalchemy/orm/properties.py +935 -0
- sqlalchemy/orm/query.py +3459 -0
- sqlalchemy/orm/relationships.py +3508 -0
- sqlalchemy/orm/scoping.py +2148 -0
- sqlalchemy/orm/session.py +5280 -0
- sqlalchemy/orm/state.py +1168 -0
- sqlalchemy/orm/state_changes.py +196 -0
- sqlalchemy/orm/strategies.py +3470 -0
- sqlalchemy/orm/strategy_options.py +2568 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +796 -0
- sqlalchemy/orm/util.py +2403 -0
- sqlalchemy/orm/writeonly.py +674 -0
- sqlalchemy/pool/__init__.py +44 -0
- sqlalchemy/pool/base.py +1524 -0
- sqlalchemy/pool/events.py +375 -0
- sqlalchemy/pool/impl.py +588 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +69 -0
- sqlalchemy/sql/__init__.py +145 -0
- sqlalchemy/sql/_dml_constructors.py +132 -0
- sqlalchemy/sql/_elements_constructors.py +1872 -0
- sqlalchemy/sql/_orm_types.py +20 -0
- sqlalchemy/sql/_py_util.py +75 -0
- sqlalchemy/sql/_selectable_constructors.py +763 -0
- sqlalchemy/sql/_typing.py +482 -0
- sqlalchemy/sql/annotation.py +587 -0
- sqlalchemy/sql/base.py +2293 -0
- sqlalchemy/sql/cache_key.py +1057 -0
- sqlalchemy/sql/coercions.py +1404 -0
- sqlalchemy/sql/compiler.py +8081 -0
- sqlalchemy/sql/crud.py +1752 -0
- sqlalchemy/sql/ddl.py +1444 -0
- sqlalchemy/sql/default_comparator.py +551 -0
- sqlalchemy/sql/dml.py +1850 -0
- sqlalchemy/sql/elements.py +5589 -0
- sqlalchemy/sql/events.py +458 -0
- sqlalchemy/sql/expression.py +159 -0
- sqlalchemy/sql/functions.py +2158 -0
- sqlalchemy/sql/lambdas.py +1442 -0
- sqlalchemy/sql/naming.py +209 -0
- sqlalchemy/sql/operators.py +2623 -0
- sqlalchemy/sql/roles.py +323 -0
- sqlalchemy/sql/schema.py +6222 -0
- sqlalchemy/sql/selectable.py +7265 -0
- sqlalchemy/sql/sqltypes.py +3930 -0
- sqlalchemy/sql/traversals.py +1024 -0
- sqlalchemy/sql/type_api.py +2368 -0
- sqlalchemy/sql/util.py +1485 -0
- sqlalchemy/sql/visitors.py +1164 -0
- sqlalchemy/testing/__init__.py +96 -0
- sqlalchemy/testing/assertions.py +994 -0
- sqlalchemy/testing/assertsql.py +520 -0
- sqlalchemy/testing/asyncio.py +135 -0
- sqlalchemy/testing/config.py +434 -0
- sqlalchemy/testing/engines.py +483 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +476 -0
- sqlalchemy/testing/fixtures/__init__.py +28 -0
- sqlalchemy/testing/fixtures/base.py +384 -0
- sqlalchemy/testing/fixtures/mypy.py +332 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +482 -0
- sqlalchemy/testing/pickleable.py +155 -0
- sqlalchemy/testing/plugin/__init__.py +6 -0
- sqlalchemy/testing/plugin/bootstrap.py +51 -0
- sqlalchemy/testing/plugin/plugin_base.py +828 -0
- sqlalchemy/testing/plugin/pytestplugin.py +892 -0
- sqlalchemy/testing/profiling.py +329 -0
- sqlalchemy/testing/provision.py +603 -0
- sqlalchemy/testing/requirements.py +1945 -0
- sqlalchemy/testing/schema.py +198 -0
- sqlalchemy/testing/suite/__init__.py +19 -0
- sqlalchemy/testing/suite/test_cte.py +237 -0
- sqlalchemy/testing/suite/test_ddl.py +389 -0
- sqlalchemy/testing/suite/test_deprecations.py +153 -0
- sqlalchemy/testing/suite/test_dialect.py +776 -0
- sqlalchemy/testing/suite/test_insert.py +630 -0
- sqlalchemy/testing/suite/test_reflection.py +3557 -0
- sqlalchemy/testing/suite/test_results.py +504 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +2010 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_types.py +2147 -0
- sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
- sqlalchemy/testing/suite/test_update_delete.py +139 -0
- sqlalchemy/testing/util.py +535 -0
- sqlalchemy/testing/warnings.py +52 -0
- sqlalchemy/types.py +74 -0
- sqlalchemy/util/__init__.py +162 -0
- sqlalchemy/util/_collections.py +712 -0
- sqlalchemy/util/_concurrency_py3k.py +288 -0
- sqlalchemy/util/_has_cy.py +40 -0
- sqlalchemy/util/_py_collections.py +541 -0
- sqlalchemy/util/compat.py +421 -0
- sqlalchemy/util/concurrency.py +110 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2203 -0
- sqlalchemy/util/preloaded.py +150 -0
- sqlalchemy/util/queue.py +322 -0
- sqlalchemy/util/tool_support.py +201 -0
- sqlalchemy/util/topological.py +120 -0
- sqlalchemy/util/typing.py +734 -0
- sqlalchemy-2.0.47.dist-info/METADATA +243 -0
- sqlalchemy-2.0.47.dist-info/RECORD +274 -0
- sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
- sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
- sqlalchemy-2.0.47.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
# testing/fixtures/sql.py
|
|
2
|
+
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of SQLAlchemy and is released under
|
|
6
|
+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
# mypy: ignore-errors
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import itertools
|
|
11
|
+
import random
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
import sqlalchemy as sa
|
|
15
|
+
from .base import TestBase
|
|
16
|
+
from .. import config
|
|
17
|
+
from .. import mock
|
|
18
|
+
from .. import provision
|
|
19
|
+
from ..assertions import eq_
|
|
20
|
+
from ..assertions import ne_
|
|
21
|
+
from ..util import adict
|
|
22
|
+
from ..util import drop_all_tables_from_metadata
|
|
23
|
+
from ... import event
|
|
24
|
+
from ... import util
|
|
25
|
+
from ...schema import sort_tables_and_constraints
|
|
26
|
+
from ...sql import visitors
|
|
27
|
+
from ...sql.elements import ClauseElement
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TablesTest(TestBase):
|
|
31
|
+
# 'once', None
|
|
32
|
+
run_setup_bind = "once"
|
|
33
|
+
|
|
34
|
+
# 'once', 'each', None
|
|
35
|
+
run_define_tables = "once"
|
|
36
|
+
|
|
37
|
+
# 'once', 'each', None
|
|
38
|
+
run_create_tables = "once"
|
|
39
|
+
|
|
40
|
+
# 'once', 'each', None
|
|
41
|
+
run_inserts = "each"
|
|
42
|
+
|
|
43
|
+
# 'each', None
|
|
44
|
+
run_deletes = "each"
|
|
45
|
+
|
|
46
|
+
# 'once', None
|
|
47
|
+
run_dispose_bind = None
|
|
48
|
+
|
|
49
|
+
bind = None
|
|
50
|
+
_tables_metadata = None
|
|
51
|
+
tables = None
|
|
52
|
+
other = None
|
|
53
|
+
sequences = None
|
|
54
|
+
|
|
55
|
+
@config.fixture(autouse=True, scope="class")
|
|
56
|
+
def _setup_tables_test_class(self):
|
|
57
|
+
cls = self.__class__
|
|
58
|
+
cls._init_class()
|
|
59
|
+
|
|
60
|
+
cls._setup_once_tables()
|
|
61
|
+
|
|
62
|
+
cls._setup_once_inserts()
|
|
63
|
+
|
|
64
|
+
yield
|
|
65
|
+
|
|
66
|
+
cls._teardown_once_metadata_bind()
|
|
67
|
+
|
|
68
|
+
@config.fixture(autouse=True, scope="function")
|
|
69
|
+
def _setup_tables_test_instance(self):
|
|
70
|
+
self._setup_each_tables()
|
|
71
|
+
self._setup_each_inserts()
|
|
72
|
+
|
|
73
|
+
yield
|
|
74
|
+
|
|
75
|
+
self._teardown_each_tables()
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def tables_test_metadata(self):
|
|
79
|
+
return self._tables_metadata
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def _init_class(cls):
|
|
83
|
+
if cls.run_define_tables == "each":
|
|
84
|
+
if cls.run_create_tables == "once":
|
|
85
|
+
cls.run_create_tables = "each"
|
|
86
|
+
assert cls.run_inserts in ("each", None)
|
|
87
|
+
|
|
88
|
+
cls.other = adict()
|
|
89
|
+
cls.tables = adict()
|
|
90
|
+
cls.sequences = adict()
|
|
91
|
+
|
|
92
|
+
cls.bind = cls.setup_bind()
|
|
93
|
+
cls._tables_metadata = sa.MetaData()
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def _setup_once_inserts(cls):
|
|
97
|
+
if cls.run_inserts == "once":
|
|
98
|
+
cls._load_fixtures()
|
|
99
|
+
with cls.bind.begin() as conn:
|
|
100
|
+
cls.insert_data(conn)
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def _setup_once_tables(cls):
|
|
104
|
+
if cls.run_define_tables == "once":
|
|
105
|
+
cls.define_tables(cls._tables_metadata)
|
|
106
|
+
if cls.run_create_tables == "once":
|
|
107
|
+
cls._tables_metadata.create_all(cls.bind)
|
|
108
|
+
cls.tables.update(cls._tables_metadata.tables)
|
|
109
|
+
cls.sequences.update(cls._tables_metadata._sequences)
|
|
110
|
+
|
|
111
|
+
def _setup_each_tables(self):
|
|
112
|
+
if self.run_define_tables == "each":
|
|
113
|
+
self.define_tables(self._tables_metadata)
|
|
114
|
+
if self.run_create_tables == "each":
|
|
115
|
+
self._tables_metadata.create_all(self.bind)
|
|
116
|
+
self.tables.update(self._tables_metadata.tables)
|
|
117
|
+
self.sequences.update(self._tables_metadata._sequences)
|
|
118
|
+
elif self.run_create_tables == "each":
|
|
119
|
+
self._tables_metadata.create_all(self.bind)
|
|
120
|
+
|
|
121
|
+
def _setup_each_inserts(self):
|
|
122
|
+
if self.run_inserts == "each":
|
|
123
|
+
self._load_fixtures()
|
|
124
|
+
with self.bind.begin() as conn:
|
|
125
|
+
self.insert_data(conn)
|
|
126
|
+
|
|
127
|
+
def _teardown_each_tables(self):
|
|
128
|
+
if self.run_define_tables == "each":
|
|
129
|
+
self.tables.clear()
|
|
130
|
+
if self.run_create_tables == "each":
|
|
131
|
+
drop_all_tables_from_metadata(self._tables_metadata, self.bind)
|
|
132
|
+
self._tables_metadata.clear()
|
|
133
|
+
elif self.run_create_tables == "each":
|
|
134
|
+
drop_all_tables_from_metadata(self._tables_metadata, self.bind)
|
|
135
|
+
|
|
136
|
+
# no need to run deletes if tables are recreated on setup
|
|
137
|
+
if (
|
|
138
|
+
self.run_define_tables != "each"
|
|
139
|
+
and self.run_create_tables == "once"
|
|
140
|
+
and self.run_deletes == "each"
|
|
141
|
+
):
|
|
142
|
+
with self.bind.begin() as conn:
|
|
143
|
+
provision.delete_from_all_tables(
|
|
144
|
+
conn, config, self._tables_metadata
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def _teardown_once_metadata_bind(cls):
|
|
149
|
+
if cls.run_create_tables:
|
|
150
|
+
drop_all_tables_from_metadata(cls._tables_metadata, cls.bind)
|
|
151
|
+
|
|
152
|
+
if cls.run_dispose_bind == "once":
|
|
153
|
+
cls.dispose_bind(cls.bind)
|
|
154
|
+
|
|
155
|
+
cls._tables_metadata.bind = None
|
|
156
|
+
|
|
157
|
+
if cls.run_setup_bind is not None:
|
|
158
|
+
cls.bind = None
|
|
159
|
+
|
|
160
|
+
@classmethod
|
|
161
|
+
def setup_bind(cls):
|
|
162
|
+
return config.db
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def dispose_bind(cls, bind):
|
|
166
|
+
if hasattr(bind, "dispose"):
|
|
167
|
+
bind.dispose()
|
|
168
|
+
elif hasattr(bind, "close"):
|
|
169
|
+
bind.close()
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def define_tables(cls, metadata):
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
@classmethod
|
|
176
|
+
def fixtures(cls):
|
|
177
|
+
return {}
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def insert_data(cls, connection):
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
def sql_count_(self, count, fn):
|
|
184
|
+
self.assert_sql_count(self.bind, fn, count)
|
|
185
|
+
|
|
186
|
+
def sql_eq_(self, callable_, statements):
|
|
187
|
+
self.assert_sql(self.bind, callable_, statements)
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def _load_fixtures(cls):
|
|
191
|
+
"""Insert rows as represented by the fixtures() method."""
|
|
192
|
+
headers, rows = {}, {}
|
|
193
|
+
for table, data in cls.fixtures().items():
|
|
194
|
+
if len(data) < 2:
|
|
195
|
+
continue
|
|
196
|
+
if isinstance(table, str):
|
|
197
|
+
table = cls.tables[table]
|
|
198
|
+
headers[table] = data[0]
|
|
199
|
+
rows[table] = data[1:]
|
|
200
|
+
for table, fks in sort_tables_and_constraints(
|
|
201
|
+
cls._tables_metadata.tables.values()
|
|
202
|
+
):
|
|
203
|
+
if table is None:
|
|
204
|
+
continue
|
|
205
|
+
if table not in headers:
|
|
206
|
+
continue
|
|
207
|
+
with cls.bind.begin() as conn:
|
|
208
|
+
conn.execute(
|
|
209
|
+
table.insert(),
|
|
210
|
+
[
|
|
211
|
+
dict(zip(headers[table], column_values))
|
|
212
|
+
for column_values in rows[table]
|
|
213
|
+
],
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class NoCache:
|
|
218
|
+
@config.fixture(autouse=True, scope="function")
|
|
219
|
+
def _disable_cache(self):
|
|
220
|
+
_cache = config.db._compiled_cache
|
|
221
|
+
config.db._compiled_cache = None
|
|
222
|
+
yield
|
|
223
|
+
config.db._compiled_cache = _cache
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class RemovesEvents:
|
|
227
|
+
@util.memoized_property
|
|
228
|
+
def _event_fns(self):
|
|
229
|
+
return set()
|
|
230
|
+
|
|
231
|
+
def event_listen(self, target, name, fn, **kw):
|
|
232
|
+
self._event_fns.add((target, name, fn))
|
|
233
|
+
event.listen(target, name, fn, **kw)
|
|
234
|
+
|
|
235
|
+
@config.fixture(autouse=True, scope="function")
|
|
236
|
+
def _remove_events(self):
|
|
237
|
+
yield
|
|
238
|
+
for key in self._event_fns:
|
|
239
|
+
event.remove(*key)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class ComputedReflectionFixtureTest(TablesTest):
|
|
243
|
+
run_inserts = run_deletes = None
|
|
244
|
+
|
|
245
|
+
__backend__ = True
|
|
246
|
+
__requires__ = ("computed_columns", "table_reflection")
|
|
247
|
+
|
|
248
|
+
regexp = re.compile(r"[\[\]\(\)\s`'\"]*")
|
|
249
|
+
|
|
250
|
+
def normalize(self, text):
|
|
251
|
+
return self.regexp.sub("", text).lower()
|
|
252
|
+
|
|
253
|
+
@classmethod
|
|
254
|
+
def define_tables(cls, metadata):
|
|
255
|
+
from ... import Integer
|
|
256
|
+
from ... import testing
|
|
257
|
+
from ...schema import Column
|
|
258
|
+
from ...schema import Computed
|
|
259
|
+
from ...schema import Table
|
|
260
|
+
|
|
261
|
+
Table(
|
|
262
|
+
"computed_default_table",
|
|
263
|
+
metadata,
|
|
264
|
+
Column("id", Integer, primary_key=True),
|
|
265
|
+
Column("normal", Integer),
|
|
266
|
+
Column("computed_col", Integer, Computed("normal + 42")),
|
|
267
|
+
Column("with_default", Integer, server_default="42"),
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
t = Table(
|
|
271
|
+
"computed_column_table",
|
|
272
|
+
metadata,
|
|
273
|
+
Column("id", Integer, primary_key=True),
|
|
274
|
+
Column("normal", Integer),
|
|
275
|
+
Column("computed_no_flag", Integer, Computed("normal + 42")),
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
if testing.requires.schemas.enabled:
|
|
279
|
+
t2 = Table(
|
|
280
|
+
"computed_column_table",
|
|
281
|
+
metadata,
|
|
282
|
+
Column("id", Integer, primary_key=True),
|
|
283
|
+
Column("normal", Integer),
|
|
284
|
+
Column("computed_no_flag", Integer, Computed("normal / 42")),
|
|
285
|
+
schema=config.test_schema,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
if testing.requires.computed_columns_virtual.enabled:
|
|
289
|
+
t.append_column(
|
|
290
|
+
Column(
|
|
291
|
+
"computed_virtual",
|
|
292
|
+
Integer,
|
|
293
|
+
Computed("normal + 2", persisted=False),
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
if testing.requires.schemas.enabled:
|
|
297
|
+
t2.append_column(
|
|
298
|
+
Column(
|
|
299
|
+
"computed_virtual",
|
|
300
|
+
Integer,
|
|
301
|
+
Computed("normal / 2", persisted=False),
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
if testing.requires.computed_columns_stored.enabled:
|
|
305
|
+
t.append_column(
|
|
306
|
+
Column(
|
|
307
|
+
"computed_stored",
|
|
308
|
+
Integer,
|
|
309
|
+
Computed("normal - 42", persisted=True),
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
if testing.requires.schemas.enabled:
|
|
313
|
+
t2.append_column(
|
|
314
|
+
Column(
|
|
315
|
+
"computed_stored",
|
|
316
|
+
Integer,
|
|
317
|
+
Computed("normal * 42", persisted=True),
|
|
318
|
+
)
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class CacheKeyFixture:
|
|
323
|
+
def _compare_equal(self, a, b, compare_values):
|
|
324
|
+
a_key = a._generate_cache_key()
|
|
325
|
+
b_key = b._generate_cache_key()
|
|
326
|
+
|
|
327
|
+
if a_key is None:
|
|
328
|
+
assert a._annotations.get("nocache")
|
|
329
|
+
|
|
330
|
+
assert b_key is None
|
|
331
|
+
else:
|
|
332
|
+
eq_(a_key.key, b_key.key)
|
|
333
|
+
eq_(hash(a_key.key), hash(b_key.key))
|
|
334
|
+
|
|
335
|
+
for a_param, b_param in zip(a_key.bindparams, b_key.bindparams):
|
|
336
|
+
assert a_param.compare(b_param, compare_values=compare_values)
|
|
337
|
+
return a_key, b_key
|
|
338
|
+
|
|
339
|
+
def _run_cache_key_fixture(self, fixture, compare_values):
|
|
340
|
+
case_a = fixture()
|
|
341
|
+
case_b = fixture()
|
|
342
|
+
|
|
343
|
+
for a, b in itertools.combinations_with_replacement(
|
|
344
|
+
range(len(case_a)), 2
|
|
345
|
+
):
|
|
346
|
+
if a == b:
|
|
347
|
+
a_key, b_key = self._compare_equal(
|
|
348
|
+
case_a[a], case_b[b], compare_values
|
|
349
|
+
)
|
|
350
|
+
if a_key is None:
|
|
351
|
+
continue
|
|
352
|
+
else:
|
|
353
|
+
a_key = case_a[a]._generate_cache_key()
|
|
354
|
+
b_key = case_b[b]._generate_cache_key()
|
|
355
|
+
|
|
356
|
+
if a_key is None or b_key is None:
|
|
357
|
+
if a_key is None:
|
|
358
|
+
assert case_a[a]._annotations.get("nocache")
|
|
359
|
+
if b_key is None:
|
|
360
|
+
assert case_b[b]._annotations.get("nocache")
|
|
361
|
+
continue
|
|
362
|
+
|
|
363
|
+
if a_key.key == b_key.key:
|
|
364
|
+
for a_param, b_param in zip(
|
|
365
|
+
a_key.bindparams, b_key.bindparams
|
|
366
|
+
):
|
|
367
|
+
if not a_param.compare(
|
|
368
|
+
b_param, compare_values=compare_values
|
|
369
|
+
):
|
|
370
|
+
break
|
|
371
|
+
else:
|
|
372
|
+
# this fails unconditionally since we could not
|
|
373
|
+
# find bound parameter values that differed.
|
|
374
|
+
# Usually we intended to get two distinct keys here
|
|
375
|
+
# so the failure will be more descriptive using the
|
|
376
|
+
# ne_() assertion.
|
|
377
|
+
ne_(a_key.key, b_key.key)
|
|
378
|
+
else:
|
|
379
|
+
ne_(a_key.key, b_key.key)
|
|
380
|
+
|
|
381
|
+
# ClauseElement-specific test to ensure the cache key
|
|
382
|
+
# collected all the bound parameters that aren't marked
|
|
383
|
+
# as "literal execute"
|
|
384
|
+
if isinstance(case_a[a], ClauseElement) and isinstance(
|
|
385
|
+
case_b[b], ClauseElement
|
|
386
|
+
):
|
|
387
|
+
assert_a_params = []
|
|
388
|
+
assert_b_params = []
|
|
389
|
+
|
|
390
|
+
for elem in visitors.iterate(case_a[a]):
|
|
391
|
+
if elem.__visit_name__ == "bindparam":
|
|
392
|
+
assert_a_params.append(elem)
|
|
393
|
+
|
|
394
|
+
for elem in visitors.iterate(case_b[b]):
|
|
395
|
+
if elem.__visit_name__ == "bindparam":
|
|
396
|
+
assert_b_params.append(elem)
|
|
397
|
+
|
|
398
|
+
# note we're asserting the order of the params as well as
|
|
399
|
+
# if there are dupes or not. ordering has to be
|
|
400
|
+
# deterministic and matches what a traversal would provide.
|
|
401
|
+
eq_(
|
|
402
|
+
sorted(a_key.bindparams, key=lambda b: b.key),
|
|
403
|
+
sorted(
|
|
404
|
+
util.unique_list(assert_a_params), key=lambda b: b.key
|
|
405
|
+
),
|
|
406
|
+
)
|
|
407
|
+
eq_(
|
|
408
|
+
sorted(b_key.bindparams, key=lambda b: b.key),
|
|
409
|
+
sorted(
|
|
410
|
+
util.unique_list(assert_b_params), key=lambda b: b.key
|
|
411
|
+
),
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
def _run_cache_key_equal_fixture(self, fixture, compare_values):
|
|
415
|
+
case_a = fixture()
|
|
416
|
+
case_b = fixture()
|
|
417
|
+
|
|
418
|
+
for a, b in itertools.combinations_with_replacement(
|
|
419
|
+
range(len(case_a)), 2
|
|
420
|
+
):
|
|
421
|
+
self._compare_equal(case_a[a], case_b[b], compare_values)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def insertmanyvalues_fixture(
|
|
425
|
+
connection, randomize_rows=False, warn_on_downgraded=False
|
|
426
|
+
):
|
|
427
|
+
dialect = connection.dialect
|
|
428
|
+
orig_dialect = dialect._deliver_insertmanyvalues_batches
|
|
429
|
+
orig_conn = connection._exec_insertmany_context
|
|
430
|
+
|
|
431
|
+
class RandomCursor:
|
|
432
|
+
__slots__ = ("cursor",)
|
|
433
|
+
|
|
434
|
+
def __init__(self, cursor):
|
|
435
|
+
self.cursor = cursor
|
|
436
|
+
|
|
437
|
+
# only this method is called by the deliver method.
|
|
438
|
+
# by not having the other methods we assert that those aren't being
|
|
439
|
+
# used
|
|
440
|
+
|
|
441
|
+
@property
|
|
442
|
+
def description(self):
|
|
443
|
+
return self.cursor.description
|
|
444
|
+
|
|
445
|
+
def fetchall(self):
|
|
446
|
+
rows = self.cursor.fetchall()
|
|
447
|
+
rows = list(rows)
|
|
448
|
+
random.shuffle(rows)
|
|
449
|
+
return rows
|
|
450
|
+
|
|
451
|
+
def _deliver_insertmanyvalues_batches(
|
|
452
|
+
connection,
|
|
453
|
+
cursor,
|
|
454
|
+
statement,
|
|
455
|
+
parameters,
|
|
456
|
+
generic_setinputsizes,
|
|
457
|
+
context,
|
|
458
|
+
):
|
|
459
|
+
if randomize_rows:
|
|
460
|
+
cursor = RandomCursor(cursor)
|
|
461
|
+
for batch in orig_dialect(
|
|
462
|
+
connection,
|
|
463
|
+
cursor,
|
|
464
|
+
statement,
|
|
465
|
+
parameters,
|
|
466
|
+
generic_setinputsizes,
|
|
467
|
+
context,
|
|
468
|
+
):
|
|
469
|
+
if warn_on_downgraded and batch.is_downgraded:
|
|
470
|
+
util.warn("Batches were downgraded for sorted INSERT")
|
|
471
|
+
|
|
472
|
+
yield batch
|
|
473
|
+
|
|
474
|
+
def _exec_insertmany_context(dialect, context):
|
|
475
|
+
with mock.patch.object(
|
|
476
|
+
dialect,
|
|
477
|
+
"_deliver_insertmanyvalues_batches",
|
|
478
|
+
new=_deliver_insertmanyvalues_batches,
|
|
479
|
+
):
|
|
480
|
+
return orig_conn(dialect, context)
|
|
481
|
+
|
|
482
|
+
connection._exec_insertmany_context = _exec_insertmany_context
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# testing/pickleable.py
|
|
2
|
+
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of SQLAlchemy and is released under
|
|
6
|
+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
# mypy: ignore-errors
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
"""Classes used in pickling tests, need to be at the module level for
|
|
11
|
+
unpickling.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from .entities import ComparableEntity
|
|
17
|
+
from ..schema import Column
|
|
18
|
+
from ..types import String
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class User(ComparableEntity):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Order(ComparableEntity):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Dingaling(ComparableEntity):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class EmailUser(User):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Address(ComparableEntity):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# TODO: these are kind of arbitrary....
|
|
42
|
+
class Child1(ComparableEntity):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Child2(ComparableEntity):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Parent(ComparableEntity):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Screen:
|
|
55
|
+
def __init__(self, obj, parent=None):
|
|
56
|
+
self.obj = obj
|
|
57
|
+
self.parent = parent
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Mixin:
|
|
61
|
+
email_address = Column(String)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AddressWMixin(Mixin, ComparableEntity):
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Foo:
|
|
69
|
+
def __init__(self, moredata, stuff="im stuff"):
|
|
70
|
+
self.data = "im data"
|
|
71
|
+
self.stuff = stuff
|
|
72
|
+
self.moredata = moredata
|
|
73
|
+
|
|
74
|
+
__hash__ = object.__hash__
|
|
75
|
+
|
|
76
|
+
def __eq__(self, other):
|
|
77
|
+
return (
|
|
78
|
+
other.data == self.data
|
|
79
|
+
and other.stuff == self.stuff
|
|
80
|
+
and other.moredata == self.moredata
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Bar:
|
|
85
|
+
def __init__(self, x, y):
|
|
86
|
+
self.x = x
|
|
87
|
+
self.y = y
|
|
88
|
+
|
|
89
|
+
__hash__ = object.__hash__
|
|
90
|
+
|
|
91
|
+
def __eq__(self, other):
|
|
92
|
+
return (
|
|
93
|
+
other.__class__ is self.__class__
|
|
94
|
+
and other.x == self.x
|
|
95
|
+
and other.y == self.y
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def __str__(self):
|
|
99
|
+
return "Bar(%d, %d)" % (self.x, self.y)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class OldSchool:
|
|
103
|
+
def __init__(self, x, y):
|
|
104
|
+
self.x = x
|
|
105
|
+
self.y = y
|
|
106
|
+
|
|
107
|
+
def __eq__(self, other):
|
|
108
|
+
return (
|
|
109
|
+
other.__class__ is self.__class__
|
|
110
|
+
and other.x == self.x
|
|
111
|
+
and other.y == self.y
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class OldSchoolWithoutCompare:
|
|
116
|
+
def __init__(self, x, y):
|
|
117
|
+
self.x = x
|
|
118
|
+
self.y = y
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class BarWithoutCompare:
|
|
122
|
+
def __init__(self, x, y):
|
|
123
|
+
self.x = x
|
|
124
|
+
self.y = y
|
|
125
|
+
|
|
126
|
+
def __str__(self):
|
|
127
|
+
return "Bar(%d, %d)" % (self.x, self.y)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class NotComparable:
|
|
131
|
+
def __init__(self, data):
|
|
132
|
+
self.data = data
|
|
133
|
+
|
|
134
|
+
def __hash__(self):
|
|
135
|
+
return id(self)
|
|
136
|
+
|
|
137
|
+
def __eq__(self, other):
|
|
138
|
+
return NotImplemented
|
|
139
|
+
|
|
140
|
+
def __ne__(self, other):
|
|
141
|
+
return NotImplemented
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class BrokenComparable:
|
|
145
|
+
def __init__(self, data):
|
|
146
|
+
self.data = data
|
|
147
|
+
|
|
148
|
+
def __hash__(self):
|
|
149
|
+
return id(self)
|
|
150
|
+
|
|
151
|
+
def __eq__(self, other):
|
|
152
|
+
raise NotImplementedError
|
|
153
|
+
|
|
154
|
+
def __ne__(self, other):
|
|
155
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# testing/plugin/bootstrap.py
|
|
2
|
+
# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
|
|
3
|
+
# <see AUTHORS file>
|
|
4
|
+
#
|
|
5
|
+
# This module is part of SQLAlchemy and is released under
|
|
6
|
+
# the MIT License: https://www.opensource.org/licenses/mit-license.php
|
|
7
|
+
# mypy: ignore-errors
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
Bootstrapper for test framework plugins.
|
|
11
|
+
|
|
12
|
+
The entire rationale for this system is to get the modules in plugin/
|
|
13
|
+
imported without importing all of the supporting library, so that we can
|
|
14
|
+
set up things for testing before coverage starts.
|
|
15
|
+
|
|
16
|
+
The rationale for all of plugin/ being *in* the supporting library in the
|
|
17
|
+
first place is so that the testing and plugin suite is available to other
|
|
18
|
+
libraries, mainly external SQLAlchemy and Alembic dialects, to make use
|
|
19
|
+
of the same test environment and standard suites available to
|
|
20
|
+
SQLAlchemy/Alembic themselves without the need to ship/install a separate
|
|
21
|
+
package outside of SQLAlchemy.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import importlib.util
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
bootstrap_file = locals()["bootstrap_file"]
|
|
32
|
+
to_bootstrap = locals()["to_bootstrap"]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def load_file_as_module(name):
|
|
36
|
+
path = os.path.join(os.path.dirname(bootstrap_file), "%s.py" % name)
|
|
37
|
+
|
|
38
|
+
spec = importlib.util.spec_from_file_location(name, path)
|
|
39
|
+
assert spec is not None
|
|
40
|
+
assert spec.loader is not None
|
|
41
|
+
mod = importlib.util.module_from_spec(spec)
|
|
42
|
+
spec.loader.exec_module(mod)
|
|
43
|
+
return mod
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if to_bootstrap == "pytest":
|
|
47
|
+
sys.modules["sqla_plugin_base"] = load_file_as_module("plugin_base")
|
|
48
|
+
sys.modules["sqla_plugin_base"].bootstrapped_as_sqlalchemy = True
|
|
49
|
+
sys.modules["sqla_pytestplugin"] = load_file_as_module("pytestplugin")
|
|
50
|
+
else:
|
|
51
|
+
raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa
|