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,1978 @@
|
|
|
1
|
+
# testing/requirements.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
|
+
"""Global database feature support policy.
|
|
11
|
+
|
|
12
|
+
Provides decorators to mark tests requiring specific feature support from the
|
|
13
|
+
target database.
|
|
14
|
+
|
|
15
|
+
External dialect test suites should subclass SuiteRequirements
|
|
16
|
+
to provide specific inclusion/exclusions.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
import platform
|
|
24
|
+
|
|
25
|
+
from . import asyncio as _test_asyncio
|
|
26
|
+
from . import exclusions
|
|
27
|
+
from .exclusions import only_on
|
|
28
|
+
from .. import create_engine
|
|
29
|
+
from .. import util
|
|
30
|
+
from ..pool import QueuePool
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Requirements:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SuiteRequirements(Requirements):
|
|
38
|
+
@property
|
|
39
|
+
def create_table(self):
|
|
40
|
+
"""target platform can emit basic CreateTable DDL."""
|
|
41
|
+
|
|
42
|
+
return exclusions.open()
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def drop_table(self):
|
|
46
|
+
"""target platform can emit basic DropTable DDL."""
|
|
47
|
+
|
|
48
|
+
return exclusions.open()
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def create_table_as(self):
|
|
52
|
+
"""target platform supports CREATE TABLE AS SELECT."""
|
|
53
|
+
|
|
54
|
+
return exclusions.closed()
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def create_temp_table_as(self):
|
|
58
|
+
"""target platform supports CREATE TEMPORARY TABLE AS SELECT."""
|
|
59
|
+
|
|
60
|
+
return exclusions.closed()
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def table_ddl_if_exists(self):
|
|
64
|
+
"""target platform supports IF NOT EXISTS / IF EXISTS for tables."""
|
|
65
|
+
|
|
66
|
+
return exclusions.closed()
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def index_ddl_if_exists(self):
|
|
70
|
+
"""target platform supports IF NOT EXISTS / IF EXISTS for indexes."""
|
|
71
|
+
|
|
72
|
+
return exclusions.closed()
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def uuid_data_type(self):
|
|
76
|
+
"""Return databases that support the UUID datatype."""
|
|
77
|
+
|
|
78
|
+
return exclusions.closed()
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def foreign_keys(self):
|
|
82
|
+
"""Target database must support foreign keys."""
|
|
83
|
+
|
|
84
|
+
return exclusions.open()
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def foreign_keys_reflect_as_index(self):
|
|
88
|
+
"""Target database creates an index that's reflected for
|
|
89
|
+
foreign keys."""
|
|
90
|
+
|
|
91
|
+
return exclusions.closed()
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def unique_index_reflect_as_unique_constraints(self):
|
|
95
|
+
"""Target database reflects unique indexes as unique constrains."""
|
|
96
|
+
|
|
97
|
+
return exclusions.closed()
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def unique_constraints_reflect_as_index(self):
|
|
101
|
+
"""Target database reflects unique constraints as indexes."""
|
|
102
|
+
|
|
103
|
+
return exclusions.closed()
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def table_value_constructor(self):
|
|
107
|
+
"""Database / dialect supports a query like:
|
|
108
|
+
|
|
109
|
+
.. sourcecode:: sql
|
|
110
|
+
|
|
111
|
+
SELECT * FROM VALUES ( (c1, c2), (c1, c2), ...)
|
|
112
|
+
AS some_table(col1, col2)
|
|
113
|
+
|
|
114
|
+
SQLAlchemy generates this with the :func:`_sql.values` function.
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
return exclusions.closed()
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def standard_cursor_sql(self):
|
|
121
|
+
"""Target database passes SQL-92 style statements to cursor.execute()
|
|
122
|
+
when a statement like select() or insert() is run.
|
|
123
|
+
|
|
124
|
+
A very small portion of dialect-level tests will ensure that certain
|
|
125
|
+
conditions are present in SQL strings, and these tests use very basic
|
|
126
|
+
SQL that will work on any SQL-like platform in order to assert results.
|
|
127
|
+
|
|
128
|
+
It's normally a given for any pep-249 DBAPI that a statement like
|
|
129
|
+
"SELECT id, name FROM table WHERE some_table.id=5" will work.
|
|
130
|
+
However, there are dialects that don't actually produce SQL Strings
|
|
131
|
+
and instead may work with symbolic objects instead, or dialects that
|
|
132
|
+
aren't working with SQL, so for those this requirement can be marked
|
|
133
|
+
as excluded.
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
return exclusions.open()
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def on_update_cascade(self):
|
|
141
|
+
"""target database must support ON UPDATE..CASCADE behavior in
|
|
142
|
+
foreign keys."""
|
|
143
|
+
|
|
144
|
+
return exclusions.open()
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def non_updating_cascade(self):
|
|
148
|
+
"""target database must *not* support ON UPDATE..CASCADE behavior in
|
|
149
|
+
foreign keys."""
|
|
150
|
+
return exclusions.closed()
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def deferrable_fks(self):
|
|
154
|
+
return exclusions.closed()
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def on_update_or_deferrable_fks(self):
|
|
158
|
+
# TODO: exclusions should be composable,
|
|
159
|
+
# somehow only_if([x, y]) isn't working here, negation/conjunctions
|
|
160
|
+
# getting confused.
|
|
161
|
+
return exclusions.only_if(
|
|
162
|
+
lambda: self.on_update_cascade.enabled
|
|
163
|
+
or self.deferrable_fks.enabled
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def queue_pool(self):
|
|
168
|
+
"""target database is using QueuePool"""
|
|
169
|
+
|
|
170
|
+
def go(config):
|
|
171
|
+
return isinstance(config.db.pool, QueuePool)
|
|
172
|
+
|
|
173
|
+
return exclusions.only_if(go)
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def self_referential_foreign_keys(self):
|
|
177
|
+
"""Target database must support self-referential foreign keys."""
|
|
178
|
+
|
|
179
|
+
return exclusions.open()
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def foreign_key_ddl(self):
|
|
183
|
+
"""Target database must support the DDL phrases for FOREIGN KEY."""
|
|
184
|
+
|
|
185
|
+
return exclusions.open()
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def named_constraints(self):
|
|
189
|
+
"""target database must support names for constraints."""
|
|
190
|
+
|
|
191
|
+
return exclusions.open()
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def implicitly_named_constraints(self):
|
|
195
|
+
"""target database must apply names to unnamed constraints."""
|
|
196
|
+
|
|
197
|
+
return exclusions.open()
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def unusual_column_name_characters(self):
|
|
201
|
+
"""target database allows column names that have unusual characters
|
|
202
|
+
in them, such as dots, spaces, slashes, or percent signs.
|
|
203
|
+
|
|
204
|
+
The column names are as always in such a case quoted, however the
|
|
205
|
+
DB still needs to support those characters in the name somehow.
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
return exclusions.open()
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def subqueries(self):
|
|
212
|
+
"""Target database must support subqueries."""
|
|
213
|
+
|
|
214
|
+
return exclusions.open()
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def offset(self):
|
|
218
|
+
"""target database can render OFFSET, or an equivalent, in a
|
|
219
|
+
SELECT.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
return exclusions.open()
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def bound_limit_offset(self):
|
|
226
|
+
"""target database can render LIMIT and/or OFFSET using a bound
|
|
227
|
+
parameter
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
return exclusions.open()
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def sql_expression_limit_offset(self):
|
|
234
|
+
"""target database can render LIMIT and/or OFFSET with a complete
|
|
235
|
+
SQL expression, such as one that uses the addition operator.
|
|
236
|
+
parameter
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
return exclusions.open()
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def parens_in_union_contained_select_w_limit_offset(self):
|
|
243
|
+
"""Target database must support parenthesized SELECT in UNION
|
|
244
|
+
when LIMIT/OFFSET is specifically present.
|
|
245
|
+
|
|
246
|
+
E.g. (SELECT ...) UNION (SELECT ..)
|
|
247
|
+
|
|
248
|
+
This is known to fail on SQLite.
|
|
249
|
+
|
|
250
|
+
"""
|
|
251
|
+
return exclusions.open()
|
|
252
|
+
|
|
253
|
+
@property
|
|
254
|
+
def parens_in_union_contained_select_wo_limit_offset(self):
|
|
255
|
+
"""Target database must support parenthesized SELECT in UNION
|
|
256
|
+
when OFFSET/LIMIT is specifically not present.
|
|
257
|
+
|
|
258
|
+
E.g. (SELECT ... LIMIT ..) UNION (SELECT .. OFFSET ..)
|
|
259
|
+
|
|
260
|
+
This is known to fail on SQLite. It also fails on Oracle
|
|
261
|
+
because without LIMIT/OFFSET, there is currently no step that
|
|
262
|
+
creates an additional subquery.
|
|
263
|
+
|
|
264
|
+
"""
|
|
265
|
+
return exclusions.open()
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def boolean_col_expressions(self):
|
|
269
|
+
"""Target database must support boolean expressions as columns"""
|
|
270
|
+
|
|
271
|
+
return exclusions.closed()
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
def nullable_booleans(self):
|
|
275
|
+
"""Target database allows boolean columns to store NULL."""
|
|
276
|
+
|
|
277
|
+
return exclusions.open()
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def nullsordering(self):
|
|
281
|
+
"""Target backends that support nulls ordering."""
|
|
282
|
+
|
|
283
|
+
return exclusions.closed()
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def standalone_binds(self):
|
|
287
|
+
"""target database/driver supports bound parameters as column
|
|
288
|
+
expressions without being in the context of a typed column.
|
|
289
|
+
"""
|
|
290
|
+
return exclusions.open()
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def standalone_null_binds_whereclause(self):
|
|
294
|
+
"""target database/driver supports bound parameters with NULL in the
|
|
295
|
+
WHERE clause, in situations where it has to be typed.
|
|
296
|
+
|
|
297
|
+
"""
|
|
298
|
+
return exclusions.open()
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def intersect(self):
|
|
302
|
+
"""Target database must support INTERSECT or equivalent."""
|
|
303
|
+
return exclusions.closed()
|
|
304
|
+
|
|
305
|
+
@property
|
|
306
|
+
def except_(self):
|
|
307
|
+
"""Target database must support EXCEPT or equivalent (i.e. MINUS)."""
|
|
308
|
+
return exclusions.closed()
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def window_functions(self):
|
|
312
|
+
"""Target database must support window functions."""
|
|
313
|
+
return exclusions.closed()
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def window_range(self):
|
|
317
|
+
"""Target backend supports RANGE in window functions with int frames"""
|
|
318
|
+
return exclusions.closed()
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def window_range_numeric(self):
|
|
322
|
+
"""Target backend supports numeric values in RANGE"""
|
|
323
|
+
return exclusions.closed()
|
|
324
|
+
|
|
325
|
+
@property
|
|
326
|
+
def window_range_non_numeric(self):
|
|
327
|
+
"""Target backend supports non-numeric values in RANGE"""
|
|
328
|
+
return exclusions.closed()
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def ctes(self):
|
|
332
|
+
"""Target database supports CTEs"""
|
|
333
|
+
|
|
334
|
+
return exclusions.closed()
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def ctes_with_update_delete(self):
|
|
338
|
+
"""target database supports CTES that ride on top of a normal UPDATE
|
|
339
|
+
or DELETE statement which refers to the CTE in a correlated subquery.
|
|
340
|
+
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
return exclusions.closed()
|
|
344
|
+
|
|
345
|
+
@property
|
|
346
|
+
def ctes_with_values(self):
|
|
347
|
+
"""target database supports CTES that ride on top of a VALUES
|
|
348
|
+
clause."""
|
|
349
|
+
|
|
350
|
+
return exclusions.closed()
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def ctes_on_dml(self):
|
|
354
|
+
"""target database supports CTES which consist of INSERT, UPDATE
|
|
355
|
+
or DELETE *within* the CTE, e.g. WITH x AS (UPDATE....)"""
|
|
356
|
+
|
|
357
|
+
return exclusions.closed()
|
|
358
|
+
|
|
359
|
+
@property
|
|
360
|
+
def autoincrement_insert(self):
|
|
361
|
+
"""target platform generates new surrogate integer primary key values
|
|
362
|
+
when insert() is executed, excluding the pk column."""
|
|
363
|
+
|
|
364
|
+
return exclusions.open()
|
|
365
|
+
|
|
366
|
+
@property
|
|
367
|
+
def fetch_rows_post_commit(self):
|
|
368
|
+
"""target platform will allow cursor.fetchone() to proceed after a
|
|
369
|
+
COMMIT.
|
|
370
|
+
|
|
371
|
+
Typically this refers to an INSERT statement with RETURNING which
|
|
372
|
+
is invoked within "autocommit". If the row can be returned
|
|
373
|
+
after the autocommit, then this rule can be open.
|
|
374
|
+
|
|
375
|
+
"""
|
|
376
|
+
|
|
377
|
+
return exclusions.open()
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def group_by_complex_expression(self):
|
|
381
|
+
"""target platform supports SQL expressions in GROUP BY
|
|
382
|
+
|
|
383
|
+
e.g.
|
|
384
|
+
|
|
385
|
+
SELECT x + y AS somelabel FROM table GROUP BY x + y
|
|
386
|
+
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
return exclusions.open()
|
|
390
|
+
|
|
391
|
+
@property
|
|
392
|
+
def sane_rowcount(self):
|
|
393
|
+
return exclusions.skip_if(
|
|
394
|
+
lambda config: not config.db.dialect.supports_sane_rowcount,
|
|
395
|
+
"driver doesn't support 'sane' rowcount",
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
@property
|
|
399
|
+
def sane_multi_rowcount(self):
|
|
400
|
+
return exclusions.fails_if(
|
|
401
|
+
lambda config: not config.db.dialect.supports_sane_multi_rowcount,
|
|
402
|
+
"driver %(driver)s %(doesnt_support)s 'sane' multi row count",
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
@property
|
|
406
|
+
def sane_rowcount_w_returning(self):
|
|
407
|
+
return exclusions.fails_if(
|
|
408
|
+
lambda config: not (
|
|
409
|
+
config.db.dialect.supports_sane_rowcount_returning
|
|
410
|
+
),
|
|
411
|
+
"driver doesn't support 'sane' rowcount when returning is on",
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
@property
|
|
415
|
+
def empty_inserts(self):
|
|
416
|
+
"""target platform supports INSERT with no values, i.e.
|
|
417
|
+
INSERT DEFAULT VALUES or equivalent."""
|
|
418
|
+
|
|
419
|
+
return exclusions.only_if(
|
|
420
|
+
lambda config: config.db.dialect.supports_empty_insert
|
|
421
|
+
or config.db.dialect.supports_default_values
|
|
422
|
+
or config.db.dialect.supports_default_metavalue,
|
|
423
|
+
"empty inserts not supported",
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
@property
|
|
427
|
+
def empty_inserts_executemany(self):
|
|
428
|
+
"""target platform supports INSERT with no values, i.e.
|
|
429
|
+
INSERT DEFAULT VALUES or equivalent, within executemany()"""
|
|
430
|
+
|
|
431
|
+
return self.empty_inserts
|
|
432
|
+
|
|
433
|
+
@property
|
|
434
|
+
def insert_from_select(self):
|
|
435
|
+
"""target platform supports INSERT from a SELECT."""
|
|
436
|
+
|
|
437
|
+
return exclusions.open()
|
|
438
|
+
|
|
439
|
+
@property
|
|
440
|
+
def delete_returning(self):
|
|
441
|
+
"""target platform supports DELETE ... RETURNING."""
|
|
442
|
+
|
|
443
|
+
return exclusions.only_if(
|
|
444
|
+
lambda config: config.db.dialect.delete_returning,
|
|
445
|
+
"%(database)s %(does_support)s 'DELETE ... RETURNING'",
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
@property
|
|
449
|
+
def insert_returning(self):
|
|
450
|
+
"""target platform supports INSERT ... RETURNING."""
|
|
451
|
+
|
|
452
|
+
return exclusions.only_if(
|
|
453
|
+
lambda config: config.db.dialect.insert_returning,
|
|
454
|
+
"%(database)s %(does_support)s 'INSERT ... RETURNING'",
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
@property
|
|
458
|
+
def update_returning(self):
|
|
459
|
+
"""target platform supports UPDATE ... RETURNING."""
|
|
460
|
+
|
|
461
|
+
return exclusions.only_if(
|
|
462
|
+
lambda config: config.db.dialect.update_returning,
|
|
463
|
+
"%(database)s %(does_support)s 'UPDATE ... RETURNING'",
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
@property
|
|
467
|
+
def insert_executemany_returning(self):
|
|
468
|
+
"""target platform supports RETURNING when INSERT is used with
|
|
469
|
+
executemany(), e.g. multiple parameter sets, indicating
|
|
470
|
+
as many rows come back as do parameter sets were passed.
|
|
471
|
+
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
return exclusions.only_if(
|
|
475
|
+
lambda config: config.db.dialect.insert_executemany_returning,
|
|
476
|
+
"%(database)s %(does_support)s 'RETURNING of "
|
|
477
|
+
"multiple rows with INSERT executemany'",
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
@property
|
|
481
|
+
def insertmanyvalues(self):
|
|
482
|
+
return exclusions.only_if(
|
|
483
|
+
lambda config: config.db.dialect.supports_multivalues_insert
|
|
484
|
+
and config.db.dialect.insert_returning
|
|
485
|
+
and config.db.dialect.use_insertmanyvalues,
|
|
486
|
+
"%(database)s %(does_support)s 'insertmanyvalues functionality",
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
@property
|
|
490
|
+
def tuple_in(self):
|
|
491
|
+
"""Target platform supports the syntax
|
|
492
|
+
"(x, y) IN ((x1, y1), (x2, y2), ...)"
|
|
493
|
+
"""
|
|
494
|
+
|
|
495
|
+
return exclusions.closed()
|
|
496
|
+
|
|
497
|
+
@property
|
|
498
|
+
def tuple_in_w_empty(self):
|
|
499
|
+
"""Target platform tuple IN w/ empty set"""
|
|
500
|
+
return self.tuple_in
|
|
501
|
+
|
|
502
|
+
@property
|
|
503
|
+
def duplicate_names_in_cursor_description(self):
|
|
504
|
+
"""target platform supports a SELECT statement that has
|
|
505
|
+
the same name repeated more than once in the columns list."""
|
|
506
|
+
|
|
507
|
+
return exclusions.open()
|
|
508
|
+
|
|
509
|
+
@property
|
|
510
|
+
def denormalized_names(self):
|
|
511
|
+
"""Target database must have 'denormalized', i.e.
|
|
512
|
+
UPPERCASE as case insensitive names."""
|
|
513
|
+
|
|
514
|
+
return exclusions.skip_if(
|
|
515
|
+
lambda config: not config.db.dialect.requires_name_normalize,
|
|
516
|
+
"Backend does not require denormalized names.",
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
@property
|
|
520
|
+
def multivalues_inserts(self):
|
|
521
|
+
"""target database must support multiple VALUES clauses in an
|
|
522
|
+
INSERT statement."""
|
|
523
|
+
|
|
524
|
+
return exclusions.skip_if(
|
|
525
|
+
lambda config: not config.db.dialect.supports_multivalues_insert,
|
|
526
|
+
"Backend does not support multirow inserts.",
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
@property
|
|
530
|
+
def implements_get_lastrowid(self):
|
|
531
|
+
"""target dialect implements the executioncontext.get_lastrowid()
|
|
532
|
+
method without reliance on RETURNING.
|
|
533
|
+
|
|
534
|
+
"""
|
|
535
|
+
return exclusions.open()
|
|
536
|
+
|
|
537
|
+
@property
|
|
538
|
+
def arraysize(self):
|
|
539
|
+
"""dialect includes the required pep-249 attribute
|
|
540
|
+
``cursor.arraysize``"""
|
|
541
|
+
|
|
542
|
+
return exclusions.open()
|
|
543
|
+
|
|
544
|
+
@property
|
|
545
|
+
def emulated_lastrowid(self):
|
|
546
|
+
"""target dialect retrieves cursor.lastrowid, or fetches
|
|
547
|
+
from a database-side function after an insert() construct executes,
|
|
548
|
+
within the get_lastrowid() method.
|
|
549
|
+
|
|
550
|
+
Only dialects that "pre-execute", or need RETURNING to get last
|
|
551
|
+
inserted id, would return closed/fail/skip for this.
|
|
552
|
+
|
|
553
|
+
"""
|
|
554
|
+
return exclusions.closed()
|
|
555
|
+
|
|
556
|
+
@property
|
|
557
|
+
def emulated_lastrowid_even_with_sequences(self):
|
|
558
|
+
"""target dialect retrieves cursor.lastrowid or an equivalent
|
|
559
|
+
after an insert() construct executes, even if the table has a
|
|
560
|
+
Sequence on it.
|
|
561
|
+
|
|
562
|
+
"""
|
|
563
|
+
return exclusions.closed()
|
|
564
|
+
|
|
565
|
+
@property
|
|
566
|
+
def dbapi_lastrowid(self):
|
|
567
|
+
"""target platform includes a 'lastrowid' accessor on the DBAPI
|
|
568
|
+
cursor object.
|
|
569
|
+
|
|
570
|
+
"""
|
|
571
|
+
return exclusions.closed()
|
|
572
|
+
|
|
573
|
+
@property
|
|
574
|
+
def views(self):
|
|
575
|
+
"""Target database must support VIEWs."""
|
|
576
|
+
|
|
577
|
+
return exclusions.closed()
|
|
578
|
+
|
|
579
|
+
@property
|
|
580
|
+
def schemas(self):
|
|
581
|
+
"""Target database must support external schemas, and have one
|
|
582
|
+
named 'test_schema'."""
|
|
583
|
+
|
|
584
|
+
return only_on(lambda config: config.db.dialect.supports_schemas)
|
|
585
|
+
|
|
586
|
+
@property
|
|
587
|
+
def cross_schema_fk_reflection(self):
|
|
588
|
+
"""target system must support reflection of inter-schema
|
|
589
|
+
foreign keys"""
|
|
590
|
+
return exclusions.closed()
|
|
591
|
+
|
|
592
|
+
@property
|
|
593
|
+
def foreign_key_constraint_name_reflection(self):
|
|
594
|
+
"""Target supports reflection of FOREIGN KEY constraints and
|
|
595
|
+
will return the name of the constraint that was used in the
|
|
596
|
+
"CONSTRAINT <name> FOREIGN KEY" DDL.
|
|
597
|
+
|
|
598
|
+
MySQL prior to version 8 and MariaDB prior to version 10.5
|
|
599
|
+
don't support this.
|
|
600
|
+
|
|
601
|
+
"""
|
|
602
|
+
return exclusions.closed()
|
|
603
|
+
|
|
604
|
+
@property
|
|
605
|
+
def implicit_default_schema(self):
|
|
606
|
+
"""target system has a strong concept of 'default' schema that can
|
|
607
|
+
be referred to implicitly.
|
|
608
|
+
|
|
609
|
+
basically, PostgreSQL.
|
|
610
|
+
|
|
611
|
+
"""
|
|
612
|
+
return exclusions.closed()
|
|
613
|
+
|
|
614
|
+
@property
|
|
615
|
+
def default_schema_name_switch(self):
|
|
616
|
+
"""target dialect implements provisioning module including
|
|
617
|
+
set_default_schema_on_connection"""
|
|
618
|
+
|
|
619
|
+
return exclusions.closed()
|
|
620
|
+
|
|
621
|
+
@property
|
|
622
|
+
def server_side_cursors(self):
|
|
623
|
+
"""Target dialect must support server side cursors."""
|
|
624
|
+
|
|
625
|
+
return exclusions.only_if(
|
|
626
|
+
[lambda config: config.db.dialect.supports_server_side_cursors],
|
|
627
|
+
"no server side cursors support",
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
@property
|
|
631
|
+
def sequences(self):
|
|
632
|
+
"""Target database must support SEQUENCEs."""
|
|
633
|
+
|
|
634
|
+
return exclusions.only_if(
|
|
635
|
+
[lambda config: config.db.dialect.supports_sequences],
|
|
636
|
+
"no sequence support",
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
@property
|
|
640
|
+
def no_sequences(self):
|
|
641
|
+
"""the opposite of "sequences", DB does not support sequences at
|
|
642
|
+
all."""
|
|
643
|
+
|
|
644
|
+
return exclusions.NotPredicate(self.sequences)
|
|
645
|
+
|
|
646
|
+
@property
|
|
647
|
+
def sequences_optional(self):
|
|
648
|
+
"""Target database supports sequences, but also optionally
|
|
649
|
+
as a means of generating new PK values."""
|
|
650
|
+
|
|
651
|
+
return exclusions.only_if(
|
|
652
|
+
[
|
|
653
|
+
lambda config: config.db.dialect.supports_sequences
|
|
654
|
+
and config.db.dialect.sequences_optional
|
|
655
|
+
],
|
|
656
|
+
"no sequence support, or sequences not optional",
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
@property
|
|
660
|
+
def supports_lastrowid(self):
|
|
661
|
+
"""target database / driver supports cursor.lastrowid as a means
|
|
662
|
+
of retrieving the last inserted primary key value.
|
|
663
|
+
|
|
664
|
+
note that if the target DB supports sequences also, this is still
|
|
665
|
+
assumed to work. This is a new use case brought on by MariaDB 10.3.
|
|
666
|
+
|
|
667
|
+
"""
|
|
668
|
+
return exclusions.only_if(
|
|
669
|
+
[lambda config: config.db.dialect.postfetch_lastrowid]
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
@property
|
|
673
|
+
def no_lastrowid_support(self):
|
|
674
|
+
"""the opposite of supports_lastrowid"""
|
|
675
|
+
return exclusions.only_if(
|
|
676
|
+
[lambda config: not config.db.dialect.postfetch_lastrowid]
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
@property
|
|
680
|
+
def reflects_pk_names(self):
|
|
681
|
+
return exclusions.closed()
|
|
682
|
+
|
|
683
|
+
@property
|
|
684
|
+
def table_reflection(self):
|
|
685
|
+
"""target database has general support for table reflection"""
|
|
686
|
+
return exclusions.open()
|
|
687
|
+
|
|
688
|
+
@property
|
|
689
|
+
def reflect_tables_no_columns(self):
|
|
690
|
+
"""target database supports creation and reflection of tables with no
|
|
691
|
+
columns, or at least tables that seem to have no columns."""
|
|
692
|
+
|
|
693
|
+
return exclusions.closed()
|
|
694
|
+
|
|
695
|
+
@property
|
|
696
|
+
def temp_table_comment_reflection(self):
|
|
697
|
+
"""indicates if database supports comments on temp tables and
|
|
698
|
+
the dialect can reflect them"""
|
|
699
|
+
return exclusions.closed()
|
|
700
|
+
|
|
701
|
+
@property
|
|
702
|
+
def comment_reflection(self):
|
|
703
|
+
"""Indicates if the database support table comment reflection"""
|
|
704
|
+
return exclusions.closed()
|
|
705
|
+
|
|
706
|
+
@property
|
|
707
|
+
def comment_reflection_full_unicode(self):
|
|
708
|
+
"""Indicates if the database support table comment reflection in the
|
|
709
|
+
full unicode range, including emoji etc.
|
|
710
|
+
"""
|
|
711
|
+
return exclusions.closed()
|
|
712
|
+
|
|
713
|
+
@property
|
|
714
|
+
def constraint_comment_reflection(self):
|
|
715
|
+
"""indicates if the database support comments on constraints
|
|
716
|
+
and their reflection"""
|
|
717
|
+
return exclusions.closed()
|
|
718
|
+
|
|
719
|
+
@property
|
|
720
|
+
def column_collation_reflection(self):
|
|
721
|
+
"""Indicates if the database support column collation reflection.
|
|
722
|
+
|
|
723
|
+
This requirement also uses ``get_order_by_collation`` to get
|
|
724
|
+
an available collation.
|
|
725
|
+
"""
|
|
726
|
+
return exclusions.closed()
|
|
727
|
+
|
|
728
|
+
@property
|
|
729
|
+
def view_column_reflection(self):
|
|
730
|
+
"""target database must support retrieval of the columns in a view,
|
|
731
|
+
similarly to how a table is inspected.
|
|
732
|
+
|
|
733
|
+
This does not include the full CREATE VIEW definition.
|
|
734
|
+
|
|
735
|
+
"""
|
|
736
|
+
return self.views
|
|
737
|
+
|
|
738
|
+
@property
|
|
739
|
+
def view_reflection(self):
|
|
740
|
+
"""target database must support inspection of the full CREATE VIEW
|
|
741
|
+
definition."""
|
|
742
|
+
return self.views
|
|
743
|
+
|
|
744
|
+
@property
|
|
745
|
+
def schema_reflection(self):
|
|
746
|
+
return self.schemas
|
|
747
|
+
|
|
748
|
+
@property
|
|
749
|
+
def schema_create_delete(self):
|
|
750
|
+
"""target database supports schema create and dropped with
|
|
751
|
+
'CREATE SCHEMA' and 'DROP SCHEMA'"""
|
|
752
|
+
return exclusions.closed()
|
|
753
|
+
|
|
754
|
+
@property
|
|
755
|
+
def primary_key_constraint_reflection(self):
|
|
756
|
+
return exclusions.open()
|
|
757
|
+
|
|
758
|
+
@property
|
|
759
|
+
def foreign_key_constraint_reflection(self):
|
|
760
|
+
return exclusions.open()
|
|
761
|
+
|
|
762
|
+
@property
|
|
763
|
+
def foreign_key_constraint_option_reflection_ondelete(self):
|
|
764
|
+
return exclusions.closed()
|
|
765
|
+
|
|
766
|
+
@property
|
|
767
|
+
def fk_constraint_option_reflection_ondelete_restrict(self):
|
|
768
|
+
return exclusions.closed()
|
|
769
|
+
|
|
770
|
+
@property
|
|
771
|
+
def fk_constraint_option_reflection_ondelete_noaction(self):
|
|
772
|
+
return exclusions.closed()
|
|
773
|
+
|
|
774
|
+
@property
|
|
775
|
+
def foreign_key_constraint_option_reflection_onupdate(self):
|
|
776
|
+
return exclusions.closed()
|
|
777
|
+
|
|
778
|
+
@property
|
|
779
|
+
def fk_constraint_option_reflection_onupdate_restrict(self):
|
|
780
|
+
return exclusions.closed()
|
|
781
|
+
|
|
782
|
+
@property
|
|
783
|
+
def temp_table_reflection(self):
|
|
784
|
+
return exclusions.open()
|
|
785
|
+
|
|
786
|
+
@property
|
|
787
|
+
def temp_table_reflect_indexes(self):
|
|
788
|
+
return self.temp_table_reflection
|
|
789
|
+
|
|
790
|
+
@property
|
|
791
|
+
def temp_table_names(self):
|
|
792
|
+
"""target dialect supports listing of temporary table names"""
|
|
793
|
+
return exclusions.closed()
|
|
794
|
+
|
|
795
|
+
@property
|
|
796
|
+
def has_temp_table(self):
|
|
797
|
+
"""target dialect supports checking a single temp table name"""
|
|
798
|
+
return exclusions.closed()
|
|
799
|
+
|
|
800
|
+
@property
|
|
801
|
+
def temporary_tables(self):
|
|
802
|
+
"""target database supports temporary tables"""
|
|
803
|
+
return exclusions.open()
|
|
804
|
+
|
|
805
|
+
@property
|
|
806
|
+
def temporary_views(self):
|
|
807
|
+
"""target database supports temporary views"""
|
|
808
|
+
return exclusions.closed()
|
|
809
|
+
|
|
810
|
+
@property
|
|
811
|
+
def create_or_replace_view(self):
|
|
812
|
+
"""target database supports CREATE OR REPLACE VIEW"""
|
|
813
|
+
return exclusions.closed()
|
|
814
|
+
|
|
815
|
+
@property
|
|
816
|
+
def index_reflection(self):
|
|
817
|
+
return exclusions.open()
|
|
818
|
+
|
|
819
|
+
@property
|
|
820
|
+
def index_reflects_included_columns(self):
|
|
821
|
+
return exclusions.closed()
|
|
822
|
+
|
|
823
|
+
@property
|
|
824
|
+
def indexes_with_ascdesc(self):
|
|
825
|
+
"""target database supports CREATE INDEX with per-column ASC/DESC."""
|
|
826
|
+
return exclusions.open()
|
|
827
|
+
|
|
828
|
+
@property
|
|
829
|
+
def reflect_indexes_with_ascdesc(self):
|
|
830
|
+
"""target database supports reflecting INDEX with per-column
|
|
831
|
+
ASC/DESC."""
|
|
832
|
+
return exclusions.open()
|
|
833
|
+
|
|
834
|
+
@property
|
|
835
|
+
def reflect_indexes_with_ascdesc_as_expression(self):
|
|
836
|
+
"""target database supports reflecting INDEX with per-column
|
|
837
|
+
ASC/DESC but reflects them as expressions (like oracle)."""
|
|
838
|
+
return exclusions.closed()
|
|
839
|
+
|
|
840
|
+
@property
|
|
841
|
+
def indexes_check_column_order(self):
|
|
842
|
+
"""target database supports CREATE INDEX with column order check."""
|
|
843
|
+
return exclusions.closed()
|
|
844
|
+
|
|
845
|
+
@property
|
|
846
|
+
def indexes_with_expressions(self):
|
|
847
|
+
"""target database supports CREATE INDEX against SQL expressions."""
|
|
848
|
+
return exclusions.closed()
|
|
849
|
+
|
|
850
|
+
@property
|
|
851
|
+
def reflect_indexes_with_expressions(self):
|
|
852
|
+
"""target database supports reflection of indexes with
|
|
853
|
+
SQL expressions."""
|
|
854
|
+
return exclusions.closed()
|
|
855
|
+
|
|
856
|
+
@property
|
|
857
|
+
def unique_constraint_reflection(self):
|
|
858
|
+
"""target dialect supports reflection of unique constraints"""
|
|
859
|
+
return exclusions.open()
|
|
860
|
+
|
|
861
|
+
@property
|
|
862
|
+
def inline_check_constraint_reflection(self):
|
|
863
|
+
"""target dialect supports reflection of inline check constraints"""
|
|
864
|
+
return exclusions.closed()
|
|
865
|
+
|
|
866
|
+
@property
|
|
867
|
+
def check_constraint_reflection(self):
|
|
868
|
+
"""target dialect supports reflection of check constraints"""
|
|
869
|
+
return exclusions.closed()
|
|
870
|
+
|
|
871
|
+
@property
|
|
872
|
+
def duplicate_key_raises_integrity_error(self):
|
|
873
|
+
"""target dialect raises IntegrityError when reporting an INSERT
|
|
874
|
+
with a primary key violation. (hint: it should)
|
|
875
|
+
|
|
876
|
+
"""
|
|
877
|
+
return exclusions.open()
|
|
878
|
+
|
|
879
|
+
@property
|
|
880
|
+
def unbounded_varchar(self):
|
|
881
|
+
"""Target database must support VARCHAR with no length"""
|
|
882
|
+
|
|
883
|
+
return exclusions.open()
|
|
884
|
+
|
|
885
|
+
@property
|
|
886
|
+
def nvarchar_types(self):
|
|
887
|
+
"""target database supports NVARCHAR and NCHAR as an actual datatype"""
|
|
888
|
+
return exclusions.closed()
|
|
889
|
+
|
|
890
|
+
@property
|
|
891
|
+
def unicode_data_no_special_types(self):
|
|
892
|
+
"""Target database/dialect can receive / deliver / compare data with
|
|
893
|
+
non-ASCII characters in plain VARCHAR, TEXT columns, without the need
|
|
894
|
+
for special "national" datatypes like NVARCHAR or similar.
|
|
895
|
+
|
|
896
|
+
"""
|
|
897
|
+
return exclusions.open()
|
|
898
|
+
|
|
899
|
+
@property
|
|
900
|
+
def unicode_data(self):
|
|
901
|
+
"""Target database/dialect must support Python unicode objects with
|
|
902
|
+
non-ASCII characters represented, delivered as bound parameters
|
|
903
|
+
as well as in result rows.
|
|
904
|
+
|
|
905
|
+
"""
|
|
906
|
+
return exclusions.open()
|
|
907
|
+
|
|
908
|
+
@property
|
|
909
|
+
def unicode_ddl(self):
|
|
910
|
+
"""Target driver must support some degree of non-ascii symbol
|
|
911
|
+
names.
|
|
912
|
+
"""
|
|
913
|
+
return exclusions.closed()
|
|
914
|
+
|
|
915
|
+
@property
|
|
916
|
+
def symbol_names_w_double_quote(self):
|
|
917
|
+
"""Target driver can create tables with a name like 'some " table'"""
|
|
918
|
+
return exclusions.open()
|
|
919
|
+
|
|
920
|
+
@property
|
|
921
|
+
def datetime_interval(self):
|
|
922
|
+
"""target dialect supports rendering of a datetime.timedelta as a
|
|
923
|
+
literal string, e.g. via the TypeEngine.literal_processor() method.
|
|
924
|
+
|
|
925
|
+
"""
|
|
926
|
+
return exclusions.closed()
|
|
927
|
+
|
|
928
|
+
@property
|
|
929
|
+
def datetime_literals(self):
|
|
930
|
+
"""target dialect supports rendering of a date, time, or datetime as a
|
|
931
|
+
literal string, e.g. via the TypeEngine.literal_processor() method.
|
|
932
|
+
|
|
933
|
+
"""
|
|
934
|
+
|
|
935
|
+
return exclusions.closed()
|
|
936
|
+
|
|
937
|
+
@property
|
|
938
|
+
def datetime(self):
|
|
939
|
+
"""target dialect supports representation of Python
|
|
940
|
+
datetime.datetime() objects."""
|
|
941
|
+
|
|
942
|
+
return exclusions.open()
|
|
943
|
+
|
|
944
|
+
@property
|
|
945
|
+
def datetime_timezone(self):
|
|
946
|
+
"""target dialect supports representation of Python
|
|
947
|
+
datetime.datetime() with tzinfo with DateTime(timezone=True)."""
|
|
948
|
+
|
|
949
|
+
return exclusions.closed()
|
|
950
|
+
|
|
951
|
+
@property
|
|
952
|
+
def time_timezone(self):
|
|
953
|
+
"""target dialect supports representation of Python
|
|
954
|
+
datetime.time() with tzinfo with Time(timezone=True)."""
|
|
955
|
+
|
|
956
|
+
return exclusions.closed()
|
|
957
|
+
|
|
958
|
+
@property
|
|
959
|
+
def date_implicit_bound(self):
|
|
960
|
+
"""target dialect when given a date object will bind it such
|
|
961
|
+
that the database server knows the object is a date, and not
|
|
962
|
+
a plain string.
|
|
963
|
+
|
|
964
|
+
"""
|
|
965
|
+
return exclusions.open()
|
|
966
|
+
|
|
967
|
+
@property
|
|
968
|
+
def time_implicit_bound(self):
|
|
969
|
+
"""target dialect when given a time object will bind it such
|
|
970
|
+
that the database server knows the object is a time, and not
|
|
971
|
+
a plain string.
|
|
972
|
+
|
|
973
|
+
"""
|
|
974
|
+
return exclusions.open()
|
|
975
|
+
|
|
976
|
+
@property
|
|
977
|
+
def datetime_implicit_bound(self):
|
|
978
|
+
"""target dialect when given a datetime object will bind it such
|
|
979
|
+
that the database server knows the object is a datetime, and not
|
|
980
|
+
a plain string.
|
|
981
|
+
|
|
982
|
+
"""
|
|
983
|
+
return exclusions.open()
|
|
984
|
+
|
|
985
|
+
@property
|
|
986
|
+
def datetime_microseconds(self):
|
|
987
|
+
"""target dialect supports representation of Python
|
|
988
|
+
datetime.datetime() with microsecond objects."""
|
|
989
|
+
|
|
990
|
+
return exclusions.open()
|
|
991
|
+
|
|
992
|
+
@property
|
|
993
|
+
def timestamp_microseconds(self):
|
|
994
|
+
"""target dialect supports representation of Python
|
|
995
|
+
datetime.datetime() with microsecond objects but only
|
|
996
|
+
if TIMESTAMP is used."""
|
|
997
|
+
return exclusions.closed()
|
|
998
|
+
|
|
999
|
+
@property
|
|
1000
|
+
def timestamp_microseconds_implicit_bound(self):
|
|
1001
|
+
"""target dialect when given a datetime object which also includes
|
|
1002
|
+
a microseconds portion when using the TIMESTAMP data type
|
|
1003
|
+
will bind it such that the database server knows
|
|
1004
|
+
the object is a datetime with microseconds, and not a plain string.
|
|
1005
|
+
|
|
1006
|
+
"""
|
|
1007
|
+
return self.timestamp_microseconds
|
|
1008
|
+
|
|
1009
|
+
@property
|
|
1010
|
+
def datetime_historic(self):
|
|
1011
|
+
"""target dialect supports representation of Python
|
|
1012
|
+
datetime.datetime() objects with historic (pre 1970) values."""
|
|
1013
|
+
|
|
1014
|
+
return exclusions.closed()
|
|
1015
|
+
|
|
1016
|
+
@property
|
|
1017
|
+
def date(self):
|
|
1018
|
+
"""target dialect supports representation of Python
|
|
1019
|
+
datetime.date() objects."""
|
|
1020
|
+
|
|
1021
|
+
return exclusions.open()
|
|
1022
|
+
|
|
1023
|
+
@property
|
|
1024
|
+
def date_coerces_from_datetime(self):
|
|
1025
|
+
"""target dialect accepts a datetime object as the target
|
|
1026
|
+
of a date column."""
|
|
1027
|
+
|
|
1028
|
+
return exclusions.open()
|
|
1029
|
+
|
|
1030
|
+
@property
|
|
1031
|
+
def date_historic(self):
|
|
1032
|
+
"""target dialect supports representation of Python
|
|
1033
|
+
datetime.datetime() objects with historic (pre 1970) values."""
|
|
1034
|
+
|
|
1035
|
+
return exclusions.closed()
|
|
1036
|
+
|
|
1037
|
+
@property
|
|
1038
|
+
def time(self):
|
|
1039
|
+
"""target dialect supports representation of Python
|
|
1040
|
+
datetime.time() objects."""
|
|
1041
|
+
|
|
1042
|
+
return exclusions.open()
|
|
1043
|
+
|
|
1044
|
+
@property
|
|
1045
|
+
def time_microseconds(self):
|
|
1046
|
+
"""target dialect supports representation of Python
|
|
1047
|
+
datetime.time() with microsecond objects."""
|
|
1048
|
+
|
|
1049
|
+
return exclusions.open()
|
|
1050
|
+
|
|
1051
|
+
@property
|
|
1052
|
+
def binary_comparisons(self):
|
|
1053
|
+
"""target database/driver can allow BLOB/BINARY fields to be compared
|
|
1054
|
+
against a bound parameter value.
|
|
1055
|
+
"""
|
|
1056
|
+
|
|
1057
|
+
return exclusions.open()
|
|
1058
|
+
|
|
1059
|
+
@property
|
|
1060
|
+
def binary_literals(self):
|
|
1061
|
+
"""target backend supports simple binary literals, e.g. an
|
|
1062
|
+
expression like:
|
|
1063
|
+
|
|
1064
|
+
.. sourcecode:: sql
|
|
1065
|
+
|
|
1066
|
+
SELECT CAST('foo' AS BINARY)
|
|
1067
|
+
|
|
1068
|
+
Where ``BINARY`` is the type emitted from :class:`.LargeBinary`,
|
|
1069
|
+
e.g. it could be ``BLOB`` or similar.
|
|
1070
|
+
|
|
1071
|
+
Basically fails on Oracle.
|
|
1072
|
+
|
|
1073
|
+
"""
|
|
1074
|
+
|
|
1075
|
+
return exclusions.open()
|
|
1076
|
+
|
|
1077
|
+
@property
|
|
1078
|
+
def autocommit(self):
|
|
1079
|
+
"""target dialect supports 'AUTOCOMMIT' as an isolation_level"""
|
|
1080
|
+
return exclusions.closed()
|
|
1081
|
+
|
|
1082
|
+
@property
|
|
1083
|
+
def skip_autocommit_rollback(self):
|
|
1084
|
+
"""target dialect supports the detect_autocommit_setting() method and
|
|
1085
|
+
uses the default implementation of do_rollback()"""
|
|
1086
|
+
|
|
1087
|
+
return exclusions.closed()
|
|
1088
|
+
|
|
1089
|
+
@property
|
|
1090
|
+
def isolation_level(self):
|
|
1091
|
+
"""target dialect supports general isolation level settings.
|
|
1092
|
+
|
|
1093
|
+
Note that this requirement, when enabled, also requires that
|
|
1094
|
+
the get_isolation_levels() method be implemented.
|
|
1095
|
+
|
|
1096
|
+
"""
|
|
1097
|
+
return exclusions.closed()
|
|
1098
|
+
|
|
1099
|
+
def get_isolation_levels(self, config):
|
|
1100
|
+
"""Return a structure of supported isolation levels for the current
|
|
1101
|
+
testing dialect.
|
|
1102
|
+
|
|
1103
|
+
The structure indicates to the testing suite what the expected
|
|
1104
|
+
"default" isolation should be, as well as the other values that
|
|
1105
|
+
are accepted. The dictionary has two keys, "default" and "supported".
|
|
1106
|
+
The "supported" key refers to a list of all supported levels and
|
|
1107
|
+
it should include AUTOCOMMIT if the dialect supports it.
|
|
1108
|
+
|
|
1109
|
+
If the :meth:`.DefaultRequirements.isolation_level` requirement is
|
|
1110
|
+
not open, then this method has no return value.
|
|
1111
|
+
|
|
1112
|
+
E.g.::
|
|
1113
|
+
|
|
1114
|
+
>>> testing.requirements.get_isolation_levels()
|
|
1115
|
+
{
|
|
1116
|
+
"default": "READ_COMMITTED",
|
|
1117
|
+
"supported": [
|
|
1118
|
+
"SERIALIZABLE", "READ UNCOMMITTED",
|
|
1119
|
+
"READ COMMITTED", "REPEATABLE READ",
|
|
1120
|
+
"AUTOCOMMIT"
|
|
1121
|
+
]
|
|
1122
|
+
}
|
|
1123
|
+
"""
|
|
1124
|
+
with config.db.connect() as conn:
|
|
1125
|
+
try:
|
|
1126
|
+
supported = conn.dialect.get_isolation_level_values(
|
|
1127
|
+
conn.connection.dbapi_connection
|
|
1128
|
+
)
|
|
1129
|
+
except NotImplementedError:
|
|
1130
|
+
return None
|
|
1131
|
+
else:
|
|
1132
|
+
return {
|
|
1133
|
+
"default": conn.dialect.default_isolation_level,
|
|
1134
|
+
"supported": supported,
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
@property
|
|
1138
|
+
def get_isolation_level_values(self):
|
|
1139
|
+
"""target dialect supports the
|
|
1140
|
+
:meth:`_engine.Dialect.get_isolation_level_values`
|
|
1141
|
+
method added in SQLAlchemy 2.0.
|
|
1142
|
+
|
|
1143
|
+
"""
|
|
1144
|
+
|
|
1145
|
+
def go(config):
|
|
1146
|
+
with config.db.connect() as conn:
|
|
1147
|
+
try:
|
|
1148
|
+
conn.dialect.get_isolation_level_values(
|
|
1149
|
+
conn.connection.dbapi_connection
|
|
1150
|
+
)
|
|
1151
|
+
except NotImplementedError:
|
|
1152
|
+
return False
|
|
1153
|
+
else:
|
|
1154
|
+
return True
|
|
1155
|
+
|
|
1156
|
+
return exclusions.only_if(go)
|
|
1157
|
+
|
|
1158
|
+
@property
|
|
1159
|
+
def dialect_level_isolation_level_param(self):
|
|
1160
|
+
"""test that the dialect allows the 'isolation_level' argument
|
|
1161
|
+
to be handled by DefaultDialect"""
|
|
1162
|
+
|
|
1163
|
+
def go(config):
|
|
1164
|
+
try:
|
|
1165
|
+
e = create_engine(
|
|
1166
|
+
config.db.url, isolation_level="READ COMMITTED"
|
|
1167
|
+
)
|
|
1168
|
+
except:
|
|
1169
|
+
return False
|
|
1170
|
+
else:
|
|
1171
|
+
return (
|
|
1172
|
+
e.dialect._on_connect_isolation_level == "READ COMMITTED"
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1175
|
+
return exclusions.only_if(go)
|
|
1176
|
+
|
|
1177
|
+
@property
|
|
1178
|
+
def array_type(self):
|
|
1179
|
+
"""Target platform implements a native ARRAY type"""
|
|
1180
|
+
return exclusions.closed()
|
|
1181
|
+
|
|
1182
|
+
@property
|
|
1183
|
+
def json_type(self):
|
|
1184
|
+
"""target platform implements a native JSON type."""
|
|
1185
|
+
|
|
1186
|
+
return exclusions.closed()
|
|
1187
|
+
|
|
1188
|
+
@property
|
|
1189
|
+
def json_array_indexes(self):
|
|
1190
|
+
"""target platform supports numeric array indexes
|
|
1191
|
+
within a JSON structure"""
|
|
1192
|
+
|
|
1193
|
+
return self.json_type
|
|
1194
|
+
|
|
1195
|
+
@property
|
|
1196
|
+
def json_index_supplementary_unicode_element(self):
|
|
1197
|
+
return exclusions.open()
|
|
1198
|
+
|
|
1199
|
+
@property
|
|
1200
|
+
def legacy_unconditional_json_extract(self):
|
|
1201
|
+
"""Backend has a JSON_EXTRACT or similar function that returns a
|
|
1202
|
+
valid JSON string in all cases.
|
|
1203
|
+
|
|
1204
|
+
Used to test a legacy feature and is not needed.
|
|
1205
|
+
|
|
1206
|
+
"""
|
|
1207
|
+
return exclusions.closed()
|
|
1208
|
+
|
|
1209
|
+
@property
|
|
1210
|
+
def precision_numerics_general(self):
|
|
1211
|
+
"""target backend has general support for moderately high-precision
|
|
1212
|
+
numerics."""
|
|
1213
|
+
return exclusions.open()
|
|
1214
|
+
|
|
1215
|
+
@property
|
|
1216
|
+
def precision_numerics_enotation_small(self):
|
|
1217
|
+
"""target backend supports Decimal() objects using E notation
|
|
1218
|
+
to represent very small values."""
|
|
1219
|
+
return exclusions.closed()
|
|
1220
|
+
|
|
1221
|
+
@property
|
|
1222
|
+
def precision_numerics_enotation_large(self):
|
|
1223
|
+
"""target backend supports Decimal() objects using E notation
|
|
1224
|
+
to represent very large values."""
|
|
1225
|
+
return exclusions.open()
|
|
1226
|
+
|
|
1227
|
+
@property
|
|
1228
|
+
def precision_numerics_many_significant_digits(self):
|
|
1229
|
+
"""target backend supports values with many digits on both sides,
|
|
1230
|
+
such as 319438950232418390.273596, 87673.594069654243
|
|
1231
|
+
|
|
1232
|
+
"""
|
|
1233
|
+
return exclusions.closed()
|
|
1234
|
+
|
|
1235
|
+
@property
|
|
1236
|
+
def cast_precision_numerics_many_significant_digits(self):
|
|
1237
|
+
"""same as precision_numerics_many_significant_digits but within the
|
|
1238
|
+
context of a CAST statement (hello MySQL)
|
|
1239
|
+
|
|
1240
|
+
"""
|
|
1241
|
+
return self.precision_numerics_many_significant_digits
|
|
1242
|
+
|
|
1243
|
+
@property
|
|
1244
|
+
def server_defaults(self):
|
|
1245
|
+
"""Target backend supports server side defaults for columns"""
|
|
1246
|
+
|
|
1247
|
+
return exclusions.closed()
|
|
1248
|
+
|
|
1249
|
+
@property
|
|
1250
|
+
def expression_server_defaults(self):
|
|
1251
|
+
"""Target backend supports server side defaults with SQL expressions
|
|
1252
|
+
for columns"""
|
|
1253
|
+
|
|
1254
|
+
return exclusions.closed()
|
|
1255
|
+
|
|
1256
|
+
@property
|
|
1257
|
+
def implicit_decimal_binds(self):
|
|
1258
|
+
"""target backend will return a selected Decimal as a Decimal, not
|
|
1259
|
+
a string.
|
|
1260
|
+
|
|
1261
|
+
e.g.::
|
|
1262
|
+
|
|
1263
|
+
expr = decimal.Decimal("15.7563")
|
|
1264
|
+
|
|
1265
|
+
value = e.scalar(select(literal(expr)))
|
|
1266
|
+
|
|
1267
|
+
assert value == expr
|
|
1268
|
+
|
|
1269
|
+
See :ticket:`4036`
|
|
1270
|
+
|
|
1271
|
+
"""
|
|
1272
|
+
|
|
1273
|
+
return exclusions.open()
|
|
1274
|
+
|
|
1275
|
+
@property
|
|
1276
|
+
def numeric_received_as_decimal_untyped(self):
|
|
1277
|
+
"""target backend will return result columns that are explicitly
|
|
1278
|
+
against NUMERIC or similar precision-numeric datatypes (not including
|
|
1279
|
+
FLOAT or INT types) as Python Decimal objects, and not as floats
|
|
1280
|
+
or ints, including when no SQLAlchemy-side typing information is
|
|
1281
|
+
associated with the statement (e.g. such as a raw SQL string).
|
|
1282
|
+
|
|
1283
|
+
This should be enabled if either the DBAPI itself returns Decimal
|
|
1284
|
+
objects, or if the dialect has set up DBAPI-specific return type
|
|
1285
|
+
handlers such that Decimal objects come back automatically.
|
|
1286
|
+
|
|
1287
|
+
"""
|
|
1288
|
+
return exclusions.open()
|
|
1289
|
+
|
|
1290
|
+
@property
|
|
1291
|
+
def nested_aggregates(self):
|
|
1292
|
+
"""target database can select an aggregate from a subquery that's
|
|
1293
|
+
also using an aggregate
|
|
1294
|
+
|
|
1295
|
+
"""
|
|
1296
|
+
return exclusions.open()
|
|
1297
|
+
|
|
1298
|
+
@property
|
|
1299
|
+
def aggregate_order_by(self):
|
|
1300
|
+
"""target database can use ORDER BY or equivalent in an aggregate
|
|
1301
|
+
function, and dialect supports aggregate_order_by().
|
|
1302
|
+
|
|
1303
|
+
"""
|
|
1304
|
+
return exclusions.closed()
|
|
1305
|
+
|
|
1306
|
+
@property
|
|
1307
|
+
def recursive_fk_cascade(self):
|
|
1308
|
+
"""target database must support ON DELETE CASCADE on a self-referential
|
|
1309
|
+
foreign key
|
|
1310
|
+
|
|
1311
|
+
"""
|
|
1312
|
+
return exclusions.open()
|
|
1313
|
+
|
|
1314
|
+
@property
|
|
1315
|
+
def precision_numerics_retains_significant_digits(self):
|
|
1316
|
+
"""A precision numeric type will return empty significant digits,
|
|
1317
|
+
i.e. a value such as 10.000 will come back in Decimal form with
|
|
1318
|
+
the .000 maintained."""
|
|
1319
|
+
|
|
1320
|
+
return exclusions.closed()
|
|
1321
|
+
|
|
1322
|
+
@property
|
|
1323
|
+
def infinity_floats(self):
|
|
1324
|
+
"""The Float type can persist and load float('inf'), float('-inf')."""
|
|
1325
|
+
|
|
1326
|
+
return exclusions.closed()
|
|
1327
|
+
|
|
1328
|
+
@property
|
|
1329
|
+
def float_or_double_precision_behaves_generically(self):
|
|
1330
|
+
return exclusions.closed()
|
|
1331
|
+
|
|
1332
|
+
@property
|
|
1333
|
+
def precision_generic_float_type(self):
|
|
1334
|
+
"""target backend will return native floating point numbers with at
|
|
1335
|
+
least seven decimal places when using the generic Float type.
|
|
1336
|
+
|
|
1337
|
+
"""
|
|
1338
|
+
return exclusions.open()
|
|
1339
|
+
|
|
1340
|
+
@property
|
|
1341
|
+
def literal_float_coercion(self):
|
|
1342
|
+
"""target backend will return the exact float value 15.7563
|
|
1343
|
+
with only four significant digits from this statement:
|
|
1344
|
+
|
|
1345
|
+
SELECT :param
|
|
1346
|
+
|
|
1347
|
+
where :param is the Python float 15.7563
|
|
1348
|
+
|
|
1349
|
+
i.e. it does not return 15.75629997253418
|
|
1350
|
+
|
|
1351
|
+
"""
|
|
1352
|
+
return exclusions.open()
|
|
1353
|
+
|
|
1354
|
+
@property
|
|
1355
|
+
def floats_to_four_decimals(self):
|
|
1356
|
+
"""target backend can return a floating-point number with four
|
|
1357
|
+
significant digits (such as 15.7563) accurately
|
|
1358
|
+
(i.e. without FP inaccuracies, such as 15.75629997253418).
|
|
1359
|
+
|
|
1360
|
+
"""
|
|
1361
|
+
return exclusions.open()
|
|
1362
|
+
|
|
1363
|
+
@property
|
|
1364
|
+
def fetch_null_from_numeric(self):
|
|
1365
|
+
"""target backend doesn't crash when you try to select a NUMERIC
|
|
1366
|
+
value that has a value of NULL.
|
|
1367
|
+
|
|
1368
|
+
Added to support Pyodbc bug #351.
|
|
1369
|
+
"""
|
|
1370
|
+
|
|
1371
|
+
return exclusions.open()
|
|
1372
|
+
|
|
1373
|
+
@property
|
|
1374
|
+
def float_is_numeric(self):
|
|
1375
|
+
"""target backend uses Numeric for Float/Dual"""
|
|
1376
|
+
|
|
1377
|
+
return exclusions.open()
|
|
1378
|
+
|
|
1379
|
+
@property
|
|
1380
|
+
def text_type(self):
|
|
1381
|
+
"""Target database must support an unbounded Text() "
|
|
1382
|
+
"type such as TEXT or CLOB"""
|
|
1383
|
+
|
|
1384
|
+
return exclusions.open()
|
|
1385
|
+
|
|
1386
|
+
@property
|
|
1387
|
+
def empty_strings_varchar(self):
|
|
1388
|
+
"""target database can persist/return an empty string with a
|
|
1389
|
+
varchar.
|
|
1390
|
+
|
|
1391
|
+
"""
|
|
1392
|
+
return exclusions.open()
|
|
1393
|
+
|
|
1394
|
+
@property
|
|
1395
|
+
def empty_strings_text(self):
|
|
1396
|
+
"""target database can persist/return an empty string with an
|
|
1397
|
+
unbounded text."""
|
|
1398
|
+
|
|
1399
|
+
return exclusions.open()
|
|
1400
|
+
|
|
1401
|
+
@property
|
|
1402
|
+
def expressions_against_unbounded_text(self):
|
|
1403
|
+
"""target database supports use of an unbounded textual field in a
|
|
1404
|
+
WHERE clause."""
|
|
1405
|
+
|
|
1406
|
+
return exclusions.open()
|
|
1407
|
+
|
|
1408
|
+
@property
|
|
1409
|
+
def selectone(self):
|
|
1410
|
+
"""target driver must support the literal statement 'select 1'"""
|
|
1411
|
+
return exclusions.open()
|
|
1412
|
+
|
|
1413
|
+
@property
|
|
1414
|
+
def savepoints(self):
|
|
1415
|
+
"""Target database must support savepoints."""
|
|
1416
|
+
|
|
1417
|
+
return exclusions.closed()
|
|
1418
|
+
|
|
1419
|
+
@property
|
|
1420
|
+
def two_phase_transactions(self):
|
|
1421
|
+
"""Target database must support two-phase transactions."""
|
|
1422
|
+
|
|
1423
|
+
return exclusions.closed()
|
|
1424
|
+
|
|
1425
|
+
@property
|
|
1426
|
+
def update_from(self):
|
|
1427
|
+
"""Target must support UPDATE..FROM syntax"""
|
|
1428
|
+
return exclusions.closed()
|
|
1429
|
+
|
|
1430
|
+
@property
|
|
1431
|
+
def delete_from(self):
|
|
1432
|
+
"""Target must support DELETE FROM..FROM or DELETE..USING syntax"""
|
|
1433
|
+
return exclusions.closed()
|
|
1434
|
+
|
|
1435
|
+
@property
|
|
1436
|
+
def update_where_target_in_subquery(self):
|
|
1437
|
+
"""Target must support UPDATE (or DELETE) where the same table is
|
|
1438
|
+
present in a subquery in the WHERE clause.
|
|
1439
|
+
|
|
1440
|
+
This is an ANSI-standard syntax that apparently MySQL can't handle,
|
|
1441
|
+
such as:
|
|
1442
|
+
|
|
1443
|
+
.. sourcecode:: sql
|
|
1444
|
+
|
|
1445
|
+
UPDATE documents SET flag=1 WHERE documents.title IN
|
|
1446
|
+
(SELECT max(documents.title) AS title
|
|
1447
|
+
FROM documents GROUP BY documents.user_id
|
|
1448
|
+
)
|
|
1449
|
+
|
|
1450
|
+
"""
|
|
1451
|
+
return exclusions.open()
|
|
1452
|
+
|
|
1453
|
+
@property
|
|
1454
|
+
def mod_operator_as_percent_sign(self):
|
|
1455
|
+
"""target database must use a plain percent '%' as the 'modulus'
|
|
1456
|
+
operator."""
|
|
1457
|
+
return exclusions.closed()
|
|
1458
|
+
|
|
1459
|
+
@property
|
|
1460
|
+
def percent_schema_names(self):
|
|
1461
|
+
"""target backend supports weird identifiers with percent signs
|
|
1462
|
+
in them, e.g. 'some % column'.
|
|
1463
|
+
|
|
1464
|
+
this is a very weird use case but often has problems because of
|
|
1465
|
+
DBAPIs that use python formatting. It's not a critical use
|
|
1466
|
+
case either.
|
|
1467
|
+
|
|
1468
|
+
"""
|
|
1469
|
+
return exclusions.closed()
|
|
1470
|
+
|
|
1471
|
+
@property
|
|
1472
|
+
def order_by_col_from_union(self):
|
|
1473
|
+
"""target database supports ordering by a column from a SELECT
|
|
1474
|
+
inside of a UNION
|
|
1475
|
+
|
|
1476
|
+
E.g.:
|
|
1477
|
+
|
|
1478
|
+
.. sourcecode:: sql
|
|
1479
|
+
|
|
1480
|
+
(SELECT id, ...) UNION (SELECT id, ...) ORDER BY id
|
|
1481
|
+
|
|
1482
|
+
"""
|
|
1483
|
+
return exclusions.open()
|
|
1484
|
+
|
|
1485
|
+
@property
|
|
1486
|
+
def order_by_label_with_expression(self):
|
|
1487
|
+
"""target backend supports ORDER BY a column label within an
|
|
1488
|
+
expression.
|
|
1489
|
+
|
|
1490
|
+
Basically this:
|
|
1491
|
+
|
|
1492
|
+
.. sourcecode:: sql
|
|
1493
|
+
|
|
1494
|
+
select data as foo from test order by foo || 'bar'
|
|
1495
|
+
|
|
1496
|
+
Lots of databases including PostgreSQL don't support this,
|
|
1497
|
+
so this is off by default.
|
|
1498
|
+
|
|
1499
|
+
"""
|
|
1500
|
+
return exclusions.closed()
|
|
1501
|
+
|
|
1502
|
+
@property
|
|
1503
|
+
def order_by_collation(self):
|
|
1504
|
+
def check(config):
|
|
1505
|
+
try:
|
|
1506
|
+
self.get_order_by_collation(config)
|
|
1507
|
+
return False
|
|
1508
|
+
except NotImplementedError:
|
|
1509
|
+
return True
|
|
1510
|
+
|
|
1511
|
+
return exclusions.skip_if(check)
|
|
1512
|
+
|
|
1513
|
+
def get_order_by_collation(self, config):
|
|
1514
|
+
raise NotImplementedError()
|
|
1515
|
+
|
|
1516
|
+
@property
|
|
1517
|
+
def unicode_connections(self):
|
|
1518
|
+
"""Target driver must support non-ASCII characters being passed at
|
|
1519
|
+
all.
|
|
1520
|
+
"""
|
|
1521
|
+
return exclusions.open()
|
|
1522
|
+
|
|
1523
|
+
@property
|
|
1524
|
+
def graceful_disconnects(self):
|
|
1525
|
+
"""Target driver must raise a DBAPI-level exception, such as
|
|
1526
|
+
InterfaceError, when the underlying connection has been closed
|
|
1527
|
+
and the execute() method is called.
|
|
1528
|
+
"""
|
|
1529
|
+
return exclusions.open()
|
|
1530
|
+
|
|
1531
|
+
@property
|
|
1532
|
+
def independent_connections(self):
|
|
1533
|
+
"""
|
|
1534
|
+
Target must support simultaneous, independent database connections.
|
|
1535
|
+
"""
|
|
1536
|
+
return exclusions.open()
|
|
1537
|
+
|
|
1538
|
+
@property
|
|
1539
|
+
def independent_readonly_connections(self):
|
|
1540
|
+
"""
|
|
1541
|
+
Target must support simultaneous, independent database connections
|
|
1542
|
+
that will be used in a readonly fashion.
|
|
1543
|
+
|
|
1544
|
+
"""
|
|
1545
|
+
return exclusions.open()
|
|
1546
|
+
|
|
1547
|
+
@property
|
|
1548
|
+
def skip_mysql_on_windows(self):
|
|
1549
|
+
"""Catchall for a large variety of MySQL on Windows failures"""
|
|
1550
|
+
return exclusions.open()
|
|
1551
|
+
|
|
1552
|
+
@property
|
|
1553
|
+
def ad_hoc_engines(self):
|
|
1554
|
+
"""Test environment must allow ad-hoc engine/connection creation.
|
|
1555
|
+
|
|
1556
|
+
No longer used in any tests; is a no-op
|
|
1557
|
+
|
|
1558
|
+
"""
|
|
1559
|
+
return exclusions.open()
|
|
1560
|
+
|
|
1561
|
+
@property
|
|
1562
|
+
def no_windows(self):
|
|
1563
|
+
return exclusions.skip_if(self._running_on_windows())
|
|
1564
|
+
|
|
1565
|
+
def _running_on_windows(self):
|
|
1566
|
+
return exclusions.LambdaPredicate(
|
|
1567
|
+
lambda: platform.system() == "Windows",
|
|
1568
|
+
description="running on Windows",
|
|
1569
|
+
)
|
|
1570
|
+
|
|
1571
|
+
@property
|
|
1572
|
+
def only_linux(self):
|
|
1573
|
+
return exclusions.only_if(self._running_on_linux())
|
|
1574
|
+
|
|
1575
|
+
def _running_on_linux(self):
|
|
1576
|
+
return exclusions.LambdaPredicate(
|
|
1577
|
+
lambda: platform.system() == "Linux",
|
|
1578
|
+
description="running on Linux",
|
|
1579
|
+
)
|
|
1580
|
+
|
|
1581
|
+
@property
|
|
1582
|
+
def timing_intensive(self):
|
|
1583
|
+
from . import config
|
|
1584
|
+
|
|
1585
|
+
return config.add_to_marker.timing_intensive
|
|
1586
|
+
|
|
1587
|
+
@property
|
|
1588
|
+
def posix(self):
|
|
1589
|
+
return exclusions.skip_if(lambda: os.name != "posix")
|
|
1590
|
+
|
|
1591
|
+
@property
|
|
1592
|
+
def memory_intensive(self):
|
|
1593
|
+
from . import config
|
|
1594
|
+
|
|
1595
|
+
return config.add_to_marker.memory_intensive
|
|
1596
|
+
|
|
1597
|
+
@property
|
|
1598
|
+
def threading_with_mock(self):
|
|
1599
|
+
"""Mark tests that use threading and mock at the same time - stability
|
|
1600
|
+
issues have been observed with coverage
|
|
1601
|
+
|
|
1602
|
+
"""
|
|
1603
|
+
return exclusions.skip_if(
|
|
1604
|
+
lambda config: config.options.has_coverage,
|
|
1605
|
+
"Stability issues with coverage",
|
|
1606
|
+
)
|
|
1607
|
+
|
|
1608
|
+
@property
|
|
1609
|
+
def sqlalchemy2_stubs(self):
|
|
1610
|
+
def check(config):
|
|
1611
|
+
try:
|
|
1612
|
+
__import__("sqlalchemy-stubs.ext.mypy")
|
|
1613
|
+
except ImportError:
|
|
1614
|
+
return False
|
|
1615
|
+
else:
|
|
1616
|
+
return True
|
|
1617
|
+
|
|
1618
|
+
return exclusions.only_if(check)
|
|
1619
|
+
|
|
1620
|
+
@property
|
|
1621
|
+
def no_sqlalchemy2_stubs(self):
|
|
1622
|
+
def check(config):
|
|
1623
|
+
try:
|
|
1624
|
+
__import__("sqlalchemy-stubs.ext.mypy")
|
|
1625
|
+
except ImportError:
|
|
1626
|
+
return False
|
|
1627
|
+
else:
|
|
1628
|
+
return True
|
|
1629
|
+
|
|
1630
|
+
return exclusions.skip_if(check)
|
|
1631
|
+
|
|
1632
|
+
@property
|
|
1633
|
+
def up_to_date_typealias_type(self):
|
|
1634
|
+
# this checks a particular quirk found in typing_extensions <=4.12.0
|
|
1635
|
+
# using older python versions like 3.10 or 3.9, we use TypeAliasType
|
|
1636
|
+
# from typing_extensions which does not provide for sufficient
|
|
1637
|
+
# introspection prior to 4.13.0
|
|
1638
|
+
def check(config):
|
|
1639
|
+
import typing
|
|
1640
|
+
import typing_extensions
|
|
1641
|
+
|
|
1642
|
+
TypeAliasType = getattr(
|
|
1643
|
+
typing, "TypeAliasType", typing_extensions.TypeAliasType
|
|
1644
|
+
)
|
|
1645
|
+
TV = typing.TypeVar("TV")
|
|
1646
|
+
TA_generic = TypeAliasType( # type: ignore
|
|
1647
|
+
"TA_generic", typing.List[TV], type_params=(TV,)
|
|
1648
|
+
)
|
|
1649
|
+
return hasattr(TA_generic[int], "__value__")
|
|
1650
|
+
|
|
1651
|
+
return exclusions.only_if(check)
|
|
1652
|
+
|
|
1653
|
+
@property
|
|
1654
|
+
def python311(self):
|
|
1655
|
+
return exclusions.only_if(
|
|
1656
|
+
lambda: util.py311, "Python 3.11 or above required"
|
|
1657
|
+
)
|
|
1658
|
+
|
|
1659
|
+
@property
|
|
1660
|
+
def python312(self):
|
|
1661
|
+
return exclusions.only_if(
|
|
1662
|
+
lambda: util.py312, "Python 3.12 or above required"
|
|
1663
|
+
)
|
|
1664
|
+
|
|
1665
|
+
@property
|
|
1666
|
+
def python314(self):
|
|
1667
|
+
return exclusions.only_if(
|
|
1668
|
+
lambda: util.py314, "Python 3.14 or above required"
|
|
1669
|
+
)
|
|
1670
|
+
|
|
1671
|
+
@property
|
|
1672
|
+
def fail_python314b1(self):
|
|
1673
|
+
return exclusions.fails_if(
|
|
1674
|
+
lambda: util.compat.py314b1, "Fails as of python 3.14.0b1"
|
|
1675
|
+
)
|
|
1676
|
+
|
|
1677
|
+
@property
|
|
1678
|
+
def not_python314(self):
|
|
1679
|
+
"""This requirement is interim to assist with backporting of
|
|
1680
|
+
issue #12405.
|
|
1681
|
+
|
|
1682
|
+
SQLAlchemy 2.0 still includes the ``await_fallback()`` method that
|
|
1683
|
+
makes use of ``asyncio.get_event_loop_policy()``. This is removed
|
|
1684
|
+
in SQLAlchemy 2.1.
|
|
1685
|
+
|
|
1686
|
+
"""
|
|
1687
|
+
return exclusions.skip_if(
|
|
1688
|
+
lambda: util.py314, "Python 3.14 or above not supported"
|
|
1689
|
+
)
|
|
1690
|
+
|
|
1691
|
+
@property
|
|
1692
|
+
def pep649(self):
|
|
1693
|
+
"""pep649 deferred evaluation of annotations without future mode"""
|
|
1694
|
+
return self.python314
|
|
1695
|
+
|
|
1696
|
+
@property
|
|
1697
|
+
def cpython(self):
|
|
1698
|
+
return exclusions.only_if(
|
|
1699
|
+
lambda: util.cpython, "cPython interpreter needed"
|
|
1700
|
+
)
|
|
1701
|
+
|
|
1702
|
+
@property
|
|
1703
|
+
def gil_enabled(self):
|
|
1704
|
+
return exclusions.only_if(
|
|
1705
|
+
lambda: not util.freethreading, "GIL-enabled build needed"
|
|
1706
|
+
)
|
|
1707
|
+
|
|
1708
|
+
@property
|
|
1709
|
+
def is64bit(self):
|
|
1710
|
+
return exclusions.only_if(lambda: util.is64bit, "64bit required")
|
|
1711
|
+
|
|
1712
|
+
@property
|
|
1713
|
+
def patch_library(self):
|
|
1714
|
+
def check_lib():
|
|
1715
|
+
try:
|
|
1716
|
+
__import__("patch")
|
|
1717
|
+
except ImportError:
|
|
1718
|
+
return False
|
|
1719
|
+
else:
|
|
1720
|
+
return True
|
|
1721
|
+
|
|
1722
|
+
return exclusions.only_if(check_lib, "patch library needed")
|
|
1723
|
+
|
|
1724
|
+
@property
|
|
1725
|
+
def predictable_gc(self):
|
|
1726
|
+
"""target platform must remove all cycles unconditionally when
|
|
1727
|
+
gc.collect() is called, as well as clean out unreferenced subclasses.
|
|
1728
|
+
|
|
1729
|
+
"""
|
|
1730
|
+
return self.cpython + self.gil_enabled
|
|
1731
|
+
|
|
1732
|
+
@property
|
|
1733
|
+
def no_coverage(self):
|
|
1734
|
+
"""Test should be skipped if coverage is enabled.
|
|
1735
|
+
|
|
1736
|
+
This is to block tests that exercise libraries that seem to be
|
|
1737
|
+
sensitive to coverage, such as PostgreSQL notice logging.
|
|
1738
|
+
|
|
1739
|
+
"""
|
|
1740
|
+
return exclusions.skip_if(
|
|
1741
|
+
lambda config: config.options.has_coverage,
|
|
1742
|
+
"Issues observed when coverage is enabled",
|
|
1743
|
+
)
|
|
1744
|
+
|
|
1745
|
+
def _has_mysql_on_windows(self, config):
|
|
1746
|
+
return False
|
|
1747
|
+
|
|
1748
|
+
def _has_mysql_fully_case_sensitive(self, config):
|
|
1749
|
+
return False
|
|
1750
|
+
|
|
1751
|
+
@property
|
|
1752
|
+
def sqlite(self):
|
|
1753
|
+
return exclusions.skip_if(lambda: not self._has_sqlite())
|
|
1754
|
+
|
|
1755
|
+
@property
|
|
1756
|
+
def cextensions(self):
|
|
1757
|
+
return exclusions.skip_if(
|
|
1758
|
+
lambda: not util.has_compiled_ext(),
|
|
1759
|
+
"Cython extensions not installed",
|
|
1760
|
+
)
|
|
1761
|
+
|
|
1762
|
+
def _has_sqlite(self):
|
|
1763
|
+
from sqlalchemy import create_engine
|
|
1764
|
+
|
|
1765
|
+
try:
|
|
1766
|
+
create_engine("sqlite://")
|
|
1767
|
+
return True
|
|
1768
|
+
except ImportError:
|
|
1769
|
+
return False
|
|
1770
|
+
|
|
1771
|
+
@property
|
|
1772
|
+
def async_dialect(self):
|
|
1773
|
+
"""dialect makes use of await_() to invoke operations on the
|
|
1774
|
+
DBAPI."""
|
|
1775
|
+
|
|
1776
|
+
return exclusions.closed()
|
|
1777
|
+
|
|
1778
|
+
@property
|
|
1779
|
+
def asyncio(self):
|
|
1780
|
+
return self.greenlet
|
|
1781
|
+
|
|
1782
|
+
@property
|
|
1783
|
+
def no_greenlet(self):
|
|
1784
|
+
def go(config):
|
|
1785
|
+
try:
|
|
1786
|
+
import greenlet # noqa: F401
|
|
1787
|
+
except ImportError:
|
|
1788
|
+
return True
|
|
1789
|
+
else:
|
|
1790
|
+
return False
|
|
1791
|
+
|
|
1792
|
+
return exclusions.only_if(go)
|
|
1793
|
+
|
|
1794
|
+
@property
|
|
1795
|
+
def greenlet(self):
|
|
1796
|
+
def go(config):
|
|
1797
|
+
if not _test_asyncio.ENABLE_ASYNCIO:
|
|
1798
|
+
return False
|
|
1799
|
+
|
|
1800
|
+
try:
|
|
1801
|
+
import greenlet # noqa: F401
|
|
1802
|
+
except ImportError:
|
|
1803
|
+
return False
|
|
1804
|
+
else:
|
|
1805
|
+
return True
|
|
1806
|
+
|
|
1807
|
+
return exclusions.only_if(go)
|
|
1808
|
+
|
|
1809
|
+
@property
|
|
1810
|
+
def computed_columns(self):
|
|
1811
|
+
"Supports computed columns"
|
|
1812
|
+
return exclusions.closed()
|
|
1813
|
+
|
|
1814
|
+
@property
|
|
1815
|
+
def computed_columns_stored(self):
|
|
1816
|
+
"Supports computed columns with `persisted=True`"
|
|
1817
|
+
return exclusions.closed()
|
|
1818
|
+
|
|
1819
|
+
@property
|
|
1820
|
+
def computed_columns_virtual(self):
|
|
1821
|
+
"Supports computed columns with `persisted=False`"
|
|
1822
|
+
return exclusions.closed()
|
|
1823
|
+
|
|
1824
|
+
@property
|
|
1825
|
+
def computed_columns_default_persisted(self):
|
|
1826
|
+
"""If the default persistence is virtual or stored when `persisted`
|
|
1827
|
+
is omitted"""
|
|
1828
|
+
return exclusions.closed()
|
|
1829
|
+
|
|
1830
|
+
@property
|
|
1831
|
+
def computed_columns_reflect_persisted(self):
|
|
1832
|
+
"""If persistence information is returned by the reflection of
|
|
1833
|
+
computed columns"""
|
|
1834
|
+
return exclusions.closed()
|
|
1835
|
+
|
|
1836
|
+
@property
|
|
1837
|
+
def supports_distinct_on(self):
|
|
1838
|
+
"""If a backend supports the DISTINCT ON in a select"""
|
|
1839
|
+
return exclusions.closed()
|
|
1840
|
+
|
|
1841
|
+
@property
|
|
1842
|
+
def supports_is_distinct_from(self):
|
|
1843
|
+
"""Supports some form of "x IS [NOT] DISTINCT FROM y" construct.
|
|
1844
|
+
Different dialects will implement their own flavour, e.g.,
|
|
1845
|
+
sqlite will emit "x IS NOT y" instead of "x IS DISTINCT FROM y".
|
|
1846
|
+
|
|
1847
|
+
.. seealso::
|
|
1848
|
+
|
|
1849
|
+
:meth:`.ColumnOperators.is_distinct_from`
|
|
1850
|
+
|
|
1851
|
+
"""
|
|
1852
|
+
return exclusions.skip_if(
|
|
1853
|
+
lambda config: not config.db.dialect.supports_is_distinct_from,
|
|
1854
|
+
"driver doesn't support an IS DISTINCT FROM construct",
|
|
1855
|
+
)
|
|
1856
|
+
|
|
1857
|
+
@property
|
|
1858
|
+
def identity_columns(self):
|
|
1859
|
+
"""If a backend supports GENERATED { ALWAYS | BY DEFAULT }
|
|
1860
|
+
AS IDENTITY"""
|
|
1861
|
+
return exclusions.closed()
|
|
1862
|
+
|
|
1863
|
+
@property
|
|
1864
|
+
def identity_columns_standard(self):
|
|
1865
|
+
"""If a backend supports GENERATED { ALWAYS | BY DEFAULT }
|
|
1866
|
+
AS IDENTITY with a standard syntax.
|
|
1867
|
+
This is mainly to exclude MSSql.
|
|
1868
|
+
"""
|
|
1869
|
+
return exclusions.closed()
|
|
1870
|
+
|
|
1871
|
+
@property
|
|
1872
|
+
def regexp_match(self):
|
|
1873
|
+
"""backend supports the regexp_match operator."""
|
|
1874
|
+
return exclusions.closed()
|
|
1875
|
+
|
|
1876
|
+
@property
|
|
1877
|
+
def regexp_replace(self):
|
|
1878
|
+
"""backend supports the regexp_replace operator."""
|
|
1879
|
+
return exclusions.closed()
|
|
1880
|
+
|
|
1881
|
+
@property
|
|
1882
|
+
def fetch_first(self):
|
|
1883
|
+
"""backend supports the fetch first clause."""
|
|
1884
|
+
return exclusions.closed()
|
|
1885
|
+
|
|
1886
|
+
@property
|
|
1887
|
+
def fetch_percent(self):
|
|
1888
|
+
"""backend supports the fetch first clause with percent."""
|
|
1889
|
+
return exclusions.closed()
|
|
1890
|
+
|
|
1891
|
+
@property
|
|
1892
|
+
def fetch_ties(self):
|
|
1893
|
+
"""backend supports the fetch first clause with ties."""
|
|
1894
|
+
return exclusions.closed()
|
|
1895
|
+
|
|
1896
|
+
@property
|
|
1897
|
+
def fetch_no_order_by(self):
|
|
1898
|
+
"""backend supports the fetch first without order by"""
|
|
1899
|
+
return exclusions.closed()
|
|
1900
|
+
|
|
1901
|
+
@property
|
|
1902
|
+
def fetch_offset_with_options(self):
|
|
1903
|
+
"""backend supports the offset when using fetch first with percent
|
|
1904
|
+
or ties. basically this is "not mssql"
|
|
1905
|
+
"""
|
|
1906
|
+
return exclusions.closed()
|
|
1907
|
+
|
|
1908
|
+
@property
|
|
1909
|
+
def fetch_expression(self):
|
|
1910
|
+
"""backend supports fetch / offset with expression in them, like
|
|
1911
|
+
|
|
1912
|
+
SELECT * FROM some_table
|
|
1913
|
+
OFFSET 1 + 1 ROWS FETCH FIRST 1 + 1 ROWS ONLY
|
|
1914
|
+
"""
|
|
1915
|
+
return exclusions.closed()
|
|
1916
|
+
|
|
1917
|
+
@property
|
|
1918
|
+
def autoincrement_without_sequence(self):
|
|
1919
|
+
"""If autoincrement=True on a column does not require an explicit
|
|
1920
|
+
sequence. This should be false only for oracle.
|
|
1921
|
+
"""
|
|
1922
|
+
return exclusions.open()
|
|
1923
|
+
|
|
1924
|
+
@property
|
|
1925
|
+
def generic_classes(self):
|
|
1926
|
+
"If X[Y] can be implemented with ``__class_getitem__``. py3.7+"
|
|
1927
|
+
return exclusions.open()
|
|
1928
|
+
|
|
1929
|
+
@property
|
|
1930
|
+
def json_deserializer_binary(self):
|
|
1931
|
+
"indicates if the json_deserializer function is called with bytes"
|
|
1932
|
+
return exclusions.closed()
|
|
1933
|
+
|
|
1934
|
+
@property
|
|
1935
|
+
def reflect_table_options(self):
|
|
1936
|
+
"""Target database must support reflecting table_options."""
|
|
1937
|
+
return exclusions.closed()
|
|
1938
|
+
|
|
1939
|
+
@property
|
|
1940
|
+
def materialized_views(self):
|
|
1941
|
+
"""Target database must support MATERIALIZED VIEWs."""
|
|
1942
|
+
return exclusions.closed()
|
|
1943
|
+
|
|
1944
|
+
@property
|
|
1945
|
+
def materialized_views_reflect_pk(self):
|
|
1946
|
+
"""Target database reflect MATERIALIZED VIEWs pks."""
|
|
1947
|
+
return exclusions.closed()
|
|
1948
|
+
|
|
1949
|
+
@property
|
|
1950
|
+
def supports_bitwise_or(self):
|
|
1951
|
+
"""Target database supports bitwise or"""
|
|
1952
|
+
return exclusions.closed()
|
|
1953
|
+
|
|
1954
|
+
@property
|
|
1955
|
+
def supports_bitwise_and(self):
|
|
1956
|
+
"""Target database supports bitwise and"""
|
|
1957
|
+
return exclusions.closed()
|
|
1958
|
+
|
|
1959
|
+
@property
|
|
1960
|
+
def supports_bitwise_not(self):
|
|
1961
|
+
"""Target database supports bitwise not"""
|
|
1962
|
+
return exclusions.closed()
|
|
1963
|
+
|
|
1964
|
+
@property
|
|
1965
|
+
def supports_bitwise_xor(self):
|
|
1966
|
+
"""Target database supports bitwise xor"""
|
|
1967
|
+
return exclusions.closed()
|
|
1968
|
+
|
|
1969
|
+
@property
|
|
1970
|
+
def supports_bitwise_shift(self):
|
|
1971
|
+
"""Target database supports bitwise left or right shift"""
|
|
1972
|
+
return exclusions.closed()
|
|
1973
|
+
|
|
1974
|
+
@property
|
|
1975
|
+
def like_escapes(self):
|
|
1976
|
+
"""Target backend supports custom ESCAPE characters
|
|
1977
|
+
with LIKE comparisons"""
|
|
1978
|
+
return exclusions.open()
|