SQLAlchemy 2.1.0b2__cp313-cp313t-win_arm64.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 +298 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +171 -0
- sqlalchemy/connectors/asyncio.py +476 -0
- sqlalchemy/connectors/pyodbc.py +250 -0
- sqlalchemy/dialects/__init__.py +62 -0
- sqlalchemy/dialects/_typing.py +30 -0
- sqlalchemy/dialects/mssql/__init__.py +89 -0
- sqlalchemy/dialects/mssql/aioodbc.py +63 -0
- sqlalchemy/dialects/mssql/base.py +4166 -0
- sqlalchemy/dialects/mssql/information_schema.py +285 -0
- sqlalchemy/dialects/mssql/json.py +140 -0
- sqlalchemy/dialects/mssql/mssqlpython.py +220 -0
- sqlalchemy/dialects/mssql/provision.py +196 -0
- sqlalchemy/dialects/mssql/pymssql.py +126 -0
- sqlalchemy/dialects/mssql/pyodbc.py +698 -0
- sqlalchemy/dialects/mysql/__init__.py +106 -0
- sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
- sqlalchemy/dialects/mysql/aiomysql.py +226 -0
- sqlalchemy/dialects/mysql/asyncmy.py +214 -0
- sqlalchemy/dialects/mysql/base.py +3877 -0
- sqlalchemy/dialects/mysql/cymysql.py +106 -0
- sqlalchemy/dialects/mysql/dml.py +279 -0
- sqlalchemy/dialects/mysql/enumerated.py +277 -0
- sqlalchemy/dialects/mysql/expression.py +146 -0
- sqlalchemy/dialects/mysql/json.py +92 -0
- sqlalchemy/dialects/mysql/mariadb.py +67 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +330 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +296 -0
- sqlalchemy/dialects/mysql/mysqldb.py +312 -0
- sqlalchemy/dialects/mysql/provision.py +153 -0
- sqlalchemy/dialects/mysql/pymysql.py +157 -0
- sqlalchemy/dialects/mysql/pyodbc.py +156 -0
- sqlalchemy/dialects/mysql/reflection.py +724 -0
- sqlalchemy/dialects/mysql/reserved_words.py +570 -0
- sqlalchemy/dialects/mysql/types.py +845 -0
- sqlalchemy/dialects/oracle/__init__.py +85 -0
- sqlalchemy/dialects/oracle/base.py +3977 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1601 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/json.py +158 -0
- sqlalchemy/dialects/oracle/oracledb.py +909 -0
- sqlalchemy/dialects/oracle/provision.py +288 -0
- sqlalchemy/dialects/oracle/types.py +367 -0
- sqlalchemy/dialects/oracle/vector.py +368 -0
- sqlalchemy/dialects/postgresql/__init__.py +171 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +229 -0
- sqlalchemy/dialects/postgresql/array.py +534 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1323 -0
- sqlalchemy/dialects/postgresql/base.py +5789 -0
- sqlalchemy/dialects/postgresql/bitstring.py +327 -0
- sqlalchemy/dialects/postgresql/dml.py +360 -0
- sqlalchemy/dialects/postgresql/ext.py +593 -0
- sqlalchemy/dialects/postgresql/hstore.py +423 -0
- sqlalchemy/dialects/postgresql/json.py +408 -0
- sqlalchemy/dialects/postgresql/named_types.py +521 -0
- sqlalchemy/dialects/postgresql/operators.py +130 -0
- sqlalchemy/dialects/postgresql/pg8000.py +670 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +344 -0
- sqlalchemy/dialects/postgresql/provision.py +184 -0
- sqlalchemy/dialects/postgresql/psycopg.py +799 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
- sqlalchemy/dialects/postgresql/ranges.py +1002 -0
- sqlalchemy/dialects/postgresql/types.py +388 -0
- sqlalchemy/dialects/sqlite/__init__.py +57 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +321 -0
- sqlalchemy/dialects/sqlite/base.py +3063 -0
- sqlalchemy/dialects/sqlite/dml.py +279 -0
- sqlalchemy/dialects/sqlite/json.py +100 -0
- sqlalchemy/dialects/sqlite/provision.py +229 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +161 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +754 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_processors_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/engine/_processors_cy.py +92 -0
- sqlalchemy/engine/_result_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/engine/_result_cy.py +633 -0
- sqlalchemy/engine/_row_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/engine/_row_cy.py +232 -0
- sqlalchemy/engine/_util_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/engine/_util_cy.py +136 -0
- sqlalchemy/engine/base.py +3354 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +877 -0
- sqlalchemy/engine/cursor.py +2421 -0
- sqlalchemy/engine/default.py +2402 -0
- sqlalchemy/engine/events.py +965 -0
- sqlalchemy/engine/interfaces.py +3495 -0
- sqlalchemy/engine/mock.py +134 -0
- sqlalchemy/engine/processors.py +82 -0
- sqlalchemy/engine/reflection.py +2100 -0
- sqlalchemy/engine/result.py +1966 -0
- sqlalchemy/engine/row.py +397 -0
- sqlalchemy/engine/strategies.py +16 -0
- sqlalchemy/engine/url.py +922 -0
- sqlalchemy/engine/util.py +156 -0
- sqlalchemy/event/__init__.py +26 -0
- sqlalchemy/event/api.py +220 -0
- sqlalchemy/event/attr.py +674 -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 +922 -0
- sqlalchemy/ext/__init__.py +11 -0
- sqlalchemy/ext/associationproxy.py +2072 -0
- sqlalchemy/ext/asyncio/__init__.py +29 -0
- sqlalchemy/ext/asyncio/base.py +281 -0
- sqlalchemy/ext/asyncio/engine.py +1487 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +994 -0
- sqlalchemy/ext/asyncio/scoping.py +1679 -0
- sqlalchemy/ext/asyncio/session.py +2007 -0
- sqlalchemy/ext/automap.py +1701 -0
- sqlalchemy/ext/baked.py +559 -0
- sqlalchemy/ext/compiler.py +600 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +560 -0
- sqlalchemy/ext/horizontal_shard.py +481 -0
- sqlalchemy/ext/hybrid.py +1877 -0
- sqlalchemy/ext/indexable.py +364 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1081 -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 +283 -0
- sqlalchemy/orm/__init__.py +176 -0
- sqlalchemy/orm/_orm_constructors.py +2694 -0
- sqlalchemy/orm/_typing.py +179 -0
- sqlalchemy/orm/attributes.py +2868 -0
- sqlalchemy/orm/base.py +976 -0
- sqlalchemy/orm/bulk_persistence.py +2152 -0
- sqlalchemy/orm/clsregistry.py +582 -0
- sqlalchemy/orm/collections.py +1568 -0
- sqlalchemy/orm/context.py +3471 -0
- sqlalchemy/orm/decl_api.py +2280 -0
- sqlalchemy/orm/decl_base.py +2309 -0
- sqlalchemy/orm/dependency.py +1306 -0
- sqlalchemy/orm/descriptor_props.py +1183 -0
- sqlalchemy/orm/dynamic.py +307 -0
- sqlalchemy/orm/evaluator.py +379 -0
- sqlalchemy/orm/events.py +3386 -0
- sqlalchemy/orm/exc.py +237 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +746 -0
- sqlalchemy/orm/interfaces.py +1589 -0
- sqlalchemy/orm/loading.py +1684 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4411 -0
- sqlalchemy/orm/path_registry.py +829 -0
- sqlalchemy/orm/persistence.py +1789 -0
- sqlalchemy/orm/properties.py +973 -0
- sqlalchemy/orm/query.py +3528 -0
- sqlalchemy/orm/relationships.py +3570 -0
- sqlalchemy/orm/scoping.py +2232 -0
- sqlalchemy/orm/session.py +5403 -0
- sqlalchemy/orm/state.py +1175 -0
- sqlalchemy/orm/state_changes.py +196 -0
- sqlalchemy/orm/strategies.py +3492 -0
- sqlalchemy/orm/strategy_options.py +2562 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +798 -0
- sqlalchemy/orm/util.py +2438 -0
- sqlalchemy/orm/writeonly.py +694 -0
- sqlalchemy/pool/__init__.py +41 -0
- sqlalchemy/pool/base.py +1522 -0
- sqlalchemy/pool/events.py +375 -0
- sqlalchemy/pool/impl.py +582 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +74 -0
- sqlalchemy/sql/__init__.py +156 -0
- sqlalchemy/sql/_annotated_cols.py +397 -0
- sqlalchemy/sql/_dml_constructors.py +132 -0
- sqlalchemy/sql/_elements_constructors.py +2164 -0
- sqlalchemy/sql/_orm_types.py +20 -0
- sqlalchemy/sql/_selectable_constructors.py +840 -0
- sqlalchemy/sql/_typing.py +487 -0
- sqlalchemy/sql/_util_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/sql/_util_cy.py +127 -0
- sqlalchemy/sql/annotation.py +590 -0
- sqlalchemy/sql/base.py +2699 -0
- sqlalchemy/sql/cache_key.py +1066 -0
- sqlalchemy/sql/coercions.py +1373 -0
- sqlalchemy/sql/compiler.py +8327 -0
- sqlalchemy/sql/crud.py +1815 -0
- sqlalchemy/sql/ddl.py +1928 -0
- sqlalchemy/sql/default_comparator.py +654 -0
- sqlalchemy/sql/dml.py +1977 -0
- sqlalchemy/sql/elements.py +6033 -0
- sqlalchemy/sql/events.py +458 -0
- sqlalchemy/sql/expression.py +172 -0
- sqlalchemy/sql/functions.py +2305 -0
- sqlalchemy/sql/lambdas.py +1443 -0
- sqlalchemy/sql/naming.py +209 -0
- sqlalchemy/sql/operators.py +2897 -0
- sqlalchemy/sql/roles.py +332 -0
- sqlalchemy/sql/schema.py +6703 -0
- sqlalchemy/sql/selectable.py +7553 -0
- sqlalchemy/sql/sqltypes.py +4093 -0
- sqlalchemy/sql/traversals.py +1042 -0
- sqlalchemy/sql/type_api.py +2446 -0
- sqlalchemy/sql/util.py +1495 -0
- sqlalchemy/sql/visitors.py +1157 -0
- sqlalchemy/testing/__init__.py +96 -0
- sqlalchemy/testing/assertions.py +1007 -0
- sqlalchemy/testing/assertsql.py +519 -0
- sqlalchemy/testing/asyncio.py +128 -0
- sqlalchemy/testing/config.py +440 -0
- sqlalchemy/testing/engines.py +483 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +476 -0
- sqlalchemy/testing/fixtures/__init__.py +30 -0
- sqlalchemy/testing/fixtures/base.py +384 -0
- sqlalchemy/testing/fixtures/mypy.py +247 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +538 -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 +613 -0
- sqlalchemy/testing/requirements.py +1978 -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 +420 -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 +660 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +2112 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_table_via_select.py +686 -0
- sqlalchemy/testing/suite/test_types.py +2271 -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 +76 -0
- sqlalchemy/util/__init__.py +158 -0
- sqlalchemy/util/_collections.py +688 -0
- sqlalchemy/util/_collections_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/util/_collections_cy.pxd +8 -0
- sqlalchemy/util/_collections_cy.py +516 -0
- sqlalchemy/util/_has_cython.py +46 -0
- sqlalchemy/util/_immutabledict_cy.cp313t-win_arm64.pyd +0 -0
- sqlalchemy/util/_immutabledict_cy.py +240 -0
- sqlalchemy/util/compat.py +299 -0
- sqlalchemy/util/concurrency.py +322 -0
- sqlalchemy/util/cython.py +79 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2320 -0
- sqlalchemy/util/preloaded.py +152 -0
- sqlalchemy/util/queue.py +304 -0
- sqlalchemy/util/tool_support.py +201 -0
- sqlalchemy/util/topological.py +120 -0
- sqlalchemy/util/typing.py +711 -0
- sqlalchemy-2.1.0b2.dist-info/METADATA +269 -0
- sqlalchemy-2.1.0b2.dist-info/RECORD +270 -0
- sqlalchemy-2.1.0b2.dist-info/WHEEL +5 -0
- sqlalchemy-2.1.0b2.dist-info/licenses/LICENSE +19 -0
- sqlalchemy-2.1.0b2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# testing/schema.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
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
from . import config
|
|
14
|
+
from . import exclusions
|
|
15
|
+
from .. import event
|
|
16
|
+
from .. import schema
|
|
17
|
+
from .. import types as sqltypes
|
|
18
|
+
from ..orm import mapped_column as _orm_mapped_column
|
|
19
|
+
from ..util import OrderedDict
|
|
20
|
+
|
|
21
|
+
__all__ = ["Table", "Column"]
|
|
22
|
+
|
|
23
|
+
table_options = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def Table(*args, **kw) -> schema.Table:
|
|
27
|
+
"""A schema.Table wrapper/hook for dialect-specific tweaks."""
|
|
28
|
+
|
|
29
|
+
# pop out local options; these are not used at the moment
|
|
30
|
+
_ = {k: kw.pop(k) for k in list(kw) if k.startswith("test_")}
|
|
31
|
+
|
|
32
|
+
kw.update(table_options)
|
|
33
|
+
|
|
34
|
+
return schema.Table(*args, **kw)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def mapped_column(*args, **kw):
|
|
38
|
+
"""An orm.mapped_column wrapper/hook for dialect-specific tweaks."""
|
|
39
|
+
|
|
40
|
+
return _schema_column(_orm_mapped_column, args, kw)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def Column(*args, **kw):
|
|
44
|
+
"""A schema.Column wrapper/hook for dialect-specific tweaks."""
|
|
45
|
+
|
|
46
|
+
return _schema_column(schema.Column, args, kw)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _schema_column(factory, args, kw):
|
|
50
|
+
test_opts = {k: kw.pop(k) for k in list(kw) if k.startswith("test_")}
|
|
51
|
+
|
|
52
|
+
if not config.requirements.foreign_key_ddl.enabled_for_config(config):
|
|
53
|
+
args = [arg for arg in args if not isinstance(arg, schema.ForeignKey)]
|
|
54
|
+
|
|
55
|
+
construct = factory(*args, **kw)
|
|
56
|
+
|
|
57
|
+
if factory is schema.Column:
|
|
58
|
+
col = construct
|
|
59
|
+
else:
|
|
60
|
+
col = construct.column
|
|
61
|
+
|
|
62
|
+
if test_opts.get("test_needs_autoincrement", False) and kw.get(
|
|
63
|
+
"primary_key", False
|
|
64
|
+
):
|
|
65
|
+
if col.default is None and col.server_default is None:
|
|
66
|
+
col.autoincrement = True
|
|
67
|
+
|
|
68
|
+
# allow any test suite to pick up on this
|
|
69
|
+
col.info["test_needs_autoincrement"] = True
|
|
70
|
+
|
|
71
|
+
# hardcoded rule for oracle; this should
|
|
72
|
+
# be moved out
|
|
73
|
+
if exclusions.against(config._current, "oracle"):
|
|
74
|
+
|
|
75
|
+
def add_seq(c, tbl):
|
|
76
|
+
c._init_items(
|
|
77
|
+
schema.Sequence(
|
|
78
|
+
_truncate_name(
|
|
79
|
+
config.db.dialect, tbl.name + "_" + c.name + "_seq"
|
|
80
|
+
),
|
|
81
|
+
optional=True,
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
event.listen(col, "after_parent_attach", add_seq, propagate=True)
|
|
86
|
+
return construct
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class eq_type_affinity:
|
|
90
|
+
"""Helper to compare types inside of datastructures based on affinity.
|
|
91
|
+
|
|
92
|
+
E.g.::
|
|
93
|
+
|
|
94
|
+
eq_(
|
|
95
|
+
inspect(connection).get_columns("foo"),
|
|
96
|
+
[
|
|
97
|
+
{
|
|
98
|
+
"name": "id",
|
|
99
|
+
"type": testing.eq_type_affinity(sqltypes.INTEGER),
|
|
100
|
+
"nullable": False,
|
|
101
|
+
"default": None,
|
|
102
|
+
"autoincrement": False,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "data",
|
|
106
|
+
"type": testing.eq_type_affinity(sqltypes.NullType),
|
|
107
|
+
"nullable": True,
|
|
108
|
+
"default": None,
|
|
109
|
+
"autoincrement": False,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
def __init__(self, target):
|
|
117
|
+
self.target = sqltypes.to_instance(target)
|
|
118
|
+
|
|
119
|
+
def __eq__(self, other):
|
|
120
|
+
return self.target._type_affinity is other._type_affinity
|
|
121
|
+
|
|
122
|
+
def __ne__(self, other):
|
|
123
|
+
return self.target._type_affinity is not other._type_affinity
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class eq_compile_type:
|
|
127
|
+
"""similar to eq_type_affinity but uses compile"""
|
|
128
|
+
|
|
129
|
+
def __init__(self, target):
|
|
130
|
+
self.target = target
|
|
131
|
+
|
|
132
|
+
def __eq__(self, other):
|
|
133
|
+
return self.target == other.compile()
|
|
134
|
+
|
|
135
|
+
def __ne__(self, other):
|
|
136
|
+
return self.target != other.compile()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class eq_clause_element:
|
|
140
|
+
"""Helper to compare SQL structures based on compare()"""
|
|
141
|
+
|
|
142
|
+
def __init__(self, target):
|
|
143
|
+
self.target = target
|
|
144
|
+
|
|
145
|
+
def __eq__(self, other):
|
|
146
|
+
return self.target.compare(other)
|
|
147
|
+
|
|
148
|
+
def __ne__(self, other):
|
|
149
|
+
return not self.target.compare(other)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _truncate_name(dialect, name):
|
|
153
|
+
if len(name) > dialect.max_identifier_length:
|
|
154
|
+
return (
|
|
155
|
+
name[0 : max(dialect.max_identifier_length - 6, 0)]
|
|
156
|
+
+ "_"
|
|
157
|
+
+ hex(hash(name) % 64)[2:]
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
return name
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def pep435_enum(name):
|
|
164
|
+
# Implements PEP 435 in the minimal fashion needed by SQLAlchemy
|
|
165
|
+
__members__ = OrderedDict()
|
|
166
|
+
|
|
167
|
+
def __init__(self, name, value, alias=None):
|
|
168
|
+
self.name = name
|
|
169
|
+
self.value = value
|
|
170
|
+
self.__members__[name] = self
|
|
171
|
+
value_to_member[value] = self
|
|
172
|
+
setattr(self.__class__, name, self)
|
|
173
|
+
if alias:
|
|
174
|
+
self.__members__[alias] = self
|
|
175
|
+
setattr(self.__class__, alias, self)
|
|
176
|
+
|
|
177
|
+
value_to_member = {}
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def get(cls, value):
|
|
181
|
+
return value_to_member[value]
|
|
182
|
+
|
|
183
|
+
someenum = type(
|
|
184
|
+
name,
|
|
185
|
+
(object,),
|
|
186
|
+
{"__members__": __members__, "__init__": __init__, "get": get},
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# getframe() trick for pickling I don't understand courtesy
|
|
190
|
+
# Python namedtuple()
|
|
191
|
+
try:
|
|
192
|
+
module = sys._getframe(1).f_globals.get("__name__", "__main__")
|
|
193
|
+
except (AttributeError, ValueError):
|
|
194
|
+
pass
|
|
195
|
+
if module is not None:
|
|
196
|
+
someenum.__module__ = module
|
|
197
|
+
|
|
198
|
+
return someenum
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# testing/suite/__init__.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
|
+
from .test_cte import * # noqa
|
|
8
|
+
from .test_ddl import * # noqa
|
|
9
|
+
from .test_dialect import * # noqa
|
|
10
|
+
from .test_insert import * # noqa
|
|
11
|
+
from .test_reflection import * # noqa
|
|
12
|
+
from .test_results import * # noqa
|
|
13
|
+
from .test_rowcount import * # noqa
|
|
14
|
+
from .test_select import * # noqa
|
|
15
|
+
from .test_sequence import * # noqa
|
|
16
|
+
from .test_table_via_select import * # noqa
|
|
17
|
+
from .test_types import * # noqa
|
|
18
|
+
from .test_unicode_ddl import * # noqa
|
|
19
|
+
from .test_update_delete import * # noqa
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# testing/suite/test_cte.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
|
+
from .. import fixtures
|
|
10
|
+
from ..assertions import eq_
|
|
11
|
+
from ..schema import Column
|
|
12
|
+
from ..schema import Table
|
|
13
|
+
from ... import column
|
|
14
|
+
from ... import ForeignKey
|
|
15
|
+
from ... import Integer
|
|
16
|
+
from ... import select
|
|
17
|
+
from ... import String
|
|
18
|
+
from ... import testing
|
|
19
|
+
from ... import values
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CTETest(fixtures.TablesTest):
|
|
23
|
+
__sparse_driver_backend__ = True
|
|
24
|
+
__requires__ = ("ctes",)
|
|
25
|
+
|
|
26
|
+
run_inserts = "each"
|
|
27
|
+
run_deletes = "each"
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def define_tables(cls, metadata):
|
|
31
|
+
Table(
|
|
32
|
+
"some_table",
|
|
33
|
+
metadata,
|
|
34
|
+
Column("id", Integer, primary_key=True),
|
|
35
|
+
Column("data", String(50)),
|
|
36
|
+
Column("parent_id", ForeignKey("some_table.id")),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
Table(
|
|
40
|
+
"some_other_table",
|
|
41
|
+
metadata,
|
|
42
|
+
Column("id", Integer, primary_key=True),
|
|
43
|
+
Column("data", String(50)),
|
|
44
|
+
Column("parent_id", Integer),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def insert_data(cls, connection):
|
|
49
|
+
connection.execute(
|
|
50
|
+
cls.tables.some_table.insert(),
|
|
51
|
+
[
|
|
52
|
+
{"id": 1, "data": "d1", "parent_id": None},
|
|
53
|
+
{"id": 2, "data": "d2", "parent_id": 1},
|
|
54
|
+
{"id": 3, "data": "d3", "parent_id": 1},
|
|
55
|
+
{"id": 4, "data": "d4", "parent_id": 3},
|
|
56
|
+
{"id": 5, "data": "d5", "parent_id": 3},
|
|
57
|
+
],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def test_select_nonrecursive_round_trip(self, connection):
|
|
61
|
+
some_table = self.tables.some_table
|
|
62
|
+
|
|
63
|
+
cte = (
|
|
64
|
+
select(some_table)
|
|
65
|
+
.where(some_table.c.data.in_(["d2", "d3", "d4"]))
|
|
66
|
+
.cte("some_cte")
|
|
67
|
+
)
|
|
68
|
+
result = connection.execute(
|
|
69
|
+
select(cte.c.data).where(cte.c.data.in_(["d4", "d5"]))
|
|
70
|
+
)
|
|
71
|
+
eq_(result.fetchall(), [("d4",)])
|
|
72
|
+
|
|
73
|
+
def test_select_recursive_round_trip(self, connection):
|
|
74
|
+
some_table = self.tables.some_table
|
|
75
|
+
|
|
76
|
+
cte = (
|
|
77
|
+
select(some_table)
|
|
78
|
+
.where(some_table.c.data.in_(["d2", "d3", "d4"]))
|
|
79
|
+
.cte("some_cte", recursive=True)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
cte_alias = cte.alias("c1")
|
|
83
|
+
st1 = some_table.alias()
|
|
84
|
+
# note that SQL Server requires this to be UNION ALL,
|
|
85
|
+
# can't be UNION
|
|
86
|
+
cte = cte.union_all(
|
|
87
|
+
select(st1).where(st1.c.id == cte_alias.c.parent_id)
|
|
88
|
+
)
|
|
89
|
+
result = connection.execute(
|
|
90
|
+
select(cte.c.data)
|
|
91
|
+
.where(cte.c.data != "d2")
|
|
92
|
+
.order_by(cte.c.data.desc())
|
|
93
|
+
)
|
|
94
|
+
eq_(
|
|
95
|
+
result.fetchall(),
|
|
96
|
+
[("d4",), ("d3",), ("d3",), ("d1",), ("d1",), ("d1",)],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def test_insert_from_select_round_trip(self, connection):
|
|
100
|
+
some_table = self.tables.some_table
|
|
101
|
+
some_other_table = self.tables.some_other_table
|
|
102
|
+
|
|
103
|
+
cte = (
|
|
104
|
+
select(some_table)
|
|
105
|
+
.where(some_table.c.data.in_(["d2", "d3", "d4"]))
|
|
106
|
+
.cte("some_cte")
|
|
107
|
+
)
|
|
108
|
+
connection.execute(
|
|
109
|
+
some_other_table.insert().from_select(
|
|
110
|
+
["id", "data", "parent_id"], select(cte)
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
eq_(
|
|
114
|
+
connection.execute(
|
|
115
|
+
select(some_other_table).order_by(some_other_table.c.id)
|
|
116
|
+
).fetchall(),
|
|
117
|
+
[(2, "d2", 1), (3, "d3", 1), (4, "d4", 3)],
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@testing.requires.ctes_with_update_delete
|
|
121
|
+
@testing.requires.update_from
|
|
122
|
+
def test_update_from_round_trip(self, connection):
|
|
123
|
+
some_table = self.tables.some_table
|
|
124
|
+
some_other_table = self.tables.some_other_table
|
|
125
|
+
|
|
126
|
+
connection.execute(
|
|
127
|
+
some_other_table.insert().from_select(
|
|
128
|
+
["id", "data", "parent_id"], select(some_table)
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
cte = (
|
|
133
|
+
select(some_table)
|
|
134
|
+
.where(some_table.c.data.in_(["d2", "d3", "d4"]))
|
|
135
|
+
.cte("some_cte")
|
|
136
|
+
)
|
|
137
|
+
connection.execute(
|
|
138
|
+
some_other_table.update()
|
|
139
|
+
.values(parent_id=5)
|
|
140
|
+
.where(some_other_table.c.data == cte.c.data)
|
|
141
|
+
)
|
|
142
|
+
eq_(
|
|
143
|
+
connection.execute(
|
|
144
|
+
select(some_other_table).order_by(some_other_table.c.id)
|
|
145
|
+
).fetchall(),
|
|
146
|
+
[
|
|
147
|
+
(1, "d1", None),
|
|
148
|
+
(2, "d2", 5),
|
|
149
|
+
(3, "d3", 5),
|
|
150
|
+
(4, "d4", 5),
|
|
151
|
+
(5, "d5", 3),
|
|
152
|
+
],
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
@testing.requires.ctes_with_update_delete
|
|
156
|
+
@testing.requires.delete_from
|
|
157
|
+
def test_delete_from_round_trip(self, connection):
|
|
158
|
+
some_table = self.tables.some_table
|
|
159
|
+
some_other_table = self.tables.some_other_table
|
|
160
|
+
|
|
161
|
+
connection.execute(
|
|
162
|
+
some_other_table.insert().from_select(
|
|
163
|
+
["id", "data", "parent_id"], select(some_table)
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
cte = (
|
|
168
|
+
select(some_table)
|
|
169
|
+
.where(some_table.c.data.in_(["d2", "d3", "d4"]))
|
|
170
|
+
.cte("some_cte")
|
|
171
|
+
)
|
|
172
|
+
connection.execute(
|
|
173
|
+
some_other_table.delete().where(
|
|
174
|
+
some_other_table.c.data == cte.c.data
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
eq_(
|
|
178
|
+
connection.execute(
|
|
179
|
+
select(some_other_table).order_by(some_other_table.c.id)
|
|
180
|
+
).fetchall(),
|
|
181
|
+
[(1, "d1", None), (5, "d5", 3)],
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
@testing.requires.ctes_with_update_delete
|
|
185
|
+
def test_delete_scalar_subq_round_trip(self, connection):
|
|
186
|
+
some_table = self.tables.some_table
|
|
187
|
+
some_other_table = self.tables.some_other_table
|
|
188
|
+
|
|
189
|
+
connection.execute(
|
|
190
|
+
some_other_table.insert().from_select(
|
|
191
|
+
["id", "data", "parent_id"], select(some_table)
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
cte = (
|
|
196
|
+
select(some_table)
|
|
197
|
+
.where(some_table.c.data.in_(["d2", "d3", "d4"]))
|
|
198
|
+
.cte("some_cte")
|
|
199
|
+
)
|
|
200
|
+
connection.execute(
|
|
201
|
+
some_other_table.delete().where(
|
|
202
|
+
some_other_table.c.data
|
|
203
|
+
== select(cte.c.data)
|
|
204
|
+
.where(cte.c.id == some_other_table.c.id)
|
|
205
|
+
.scalar_subquery()
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
eq_(
|
|
209
|
+
connection.execute(
|
|
210
|
+
select(some_other_table).order_by(some_other_table.c.id)
|
|
211
|
+
).fetchall(),
|
|
212
|
+
[(1, "d1", None), (5, "d5", 3)],
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
@testing.variation("values_named", [True, False])
|
|
216
|
+
@testing.variation("cte_named", [True, False])
|
|
217
|
+
@testing.variation("literal_binds", [True, False])
|
|
218
|
+
@testing.requires.ctes_with_values
|
|
219
|
+
def test_values_named_via_cte(
|
|
220
|
+
self, connection, values_named, cte_named, literal_binds
|
|
221
|
+
):
|
|
222
|
+
|
|
223
|
+
cte1 = (
|
|
224
|
+
values(
|
|
225
|
+
column("col1", String),
|
|
226
|
+
column("col2", Integer),
|
|
227
|
+
literal_binds=bool(literal_binds),
|
|
228
|
+
name="some name" if values_named else None,
|
|
229
|
+
)
|
|
230
|
+
.data([("a", 2), ("b", 3)])
|
|
231
|
+
.cte("cte1" if cte_named else None)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
stmt = select(cte1)
|
|
235
|
+
|
|
236
|
+
rows = connection.execute(stmt).all()
|
|
237
|
+
eq_(rows, [("a", 2), ("b", 3)])
|