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,285 @@
|
|
|
1
|
+
# dialects/mssql/information_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 ... import cast
|
|
10
|
+
from ... import Column
|
|
11
|
+
from ... import MetaData
|
|
12
|
+
from ... import Table
|
|
13
|
+
from ...ext.compiler import compiles
|
|
14
|
+
from ...sql import expression
|
|
15
|
+
from ...types import Boolean
|
|
16
|
+
from ...types import Integer
|
|
17
|
+
from ...types import Numeric
|
|
18
|
+
from ...types import NVARCHAR
|
|
19
|
+
from ...types import String
|
|
20
|
+
from ...types import TypeDecorator
|
|
21
|
+
from ...types import Unicode
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
ischema = MetaData()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CoerceUnicode(TypeDecorator):
|
|
28
|
+
impl = Unicode
|
|
29
|
+
cache_ok = True
|
|
30
|
+
|
|
31
|
+
def bind_expression(self, bindvalue):
|
|
32
|
+
return _cast_on_2005(bindvalue)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _cast_on_2005(expression.ColumnElement):
|
|
36
|
+
def __init__(self, bindvalue):
|
|
37
|
+
self.bindvalue = bindvalue
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@compiles(_cast_on_2005)
|
|
41
|
+
def _compile(element, compiler, **kw):
|
|
42
|
+
from . import base
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
compiler.dialect.server_version_info is None
|
|
46
|
+
or compiler.dialect.server_version_info < base.MS_2005_VERSION
|
|
47
|
+
):
|
|
48
|
+
return compiler.process(element.bindvalue, **kw)
|
|
49
|
+
else:
|
|
50
|
+
return compiler.process(cast(element.bindvalue, Unicode), **kw)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
schemata = Table(
|
|
54
|
+
"SCHEMATA",
|
|
55
|
+
ischema,
|
|
56
|
+
Column("CATALOG_NAME", CoerceUnicode, key="catalog_name"),
|
|
57
|
+
Column("SCHEMA_NAME", CoerceUnicode, key="schema_name"),
|
|
58
|
+
Column("SCHEMA_OWNER", CoerceUnicode, key="schema_owner"),
|
|
59
|
+
schema="INFORMATION_SCHEMA",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
tables = Table(
|
|
63
|
+
"TABLES",
|
|
64
|
+
ischema,
|
|
65
|
+
Column("TABLE_CATALOG", CoerceUnicode, key="table_catalog"),
|
|
66
|
+
Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
|
|
67
|
+
Column("TABLE_NAME", CoerceUnicode, key="table_name"),
|
|
68
|
+
Column("TABLE_TYPE", CoerceUnicode, key="table_type"),
|
|
69
|
+
schema="INFORMATION_SCHEMA",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
columns = Table(
|
|
73
|
+
"COLUMNS",
|
|
74
|
+
ischema,
|
|
75
|
+
Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
|
|
76
|
+
Column("TABLE_NAME", CoerceUnicode, key="table_name"),
|
|
77
|
+
Column("COLUMN_NAME", CoerceUnicode, key="column_name"),
|
|
78
|
+
Column("IS_NULLABLE", Integer, key="is_nullable"),
|
|
79
|
+
Column("DATA_TYPE", String, key="data_type"),
|
|
80
|
+
Column("ORDINAL_POSITION", Integer, key="ordinal_position"),
|
|
81
|
+
Column(
|
|
82
|
+
"CHARACTER_MAXIMUM_LENGTH", Integer, key="character_maximum_length"
|
|
83
|
+
),
|
|
84
|
+
Column("NUMERIC_PRECISION", Integer, key="numeric_precision"),
|
|
85
|
+
Column("NUMERIC_SCALE", Integer, key="numeric_scale"),
|
|
86
|
+
Column("COLUMN_DEFAULT", Integer, key="column_default"),
|
|
87
|
+
Column("COLLATION_NAME", String, key="collation_name"),
|
|
88
|
+
schema="INFORMATION_SCHEMA",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
sys_columns = Table(
|
|
92
|
+
"columns",
|
|
93
|
+
ischema,
|
|
94
|
+
Column("object_id", Integer),
|
|
95
|
+
Column("name", CoerceUnicode),
|
|
96
|
+
Column("column_id", Integer),
|
|
97
|
+
Column("default_object_id", Integer),
|
|
98
|
+
Column("user_type_id", Integer),
|
|
99
|
+
Column("is_nullable", Integer),
|
|
100
|
+
Column("ordinal_position", Integer),
|
|
101
|
+
Column("max_length", Integer),
|
|
102
|
+
Column("precision", Integer),
|
|
103
|
+
Column("scale", Integer),
|
|
104
|
+
Column("collation_name", String),
|
|
105
|
+
schema="sys",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
sys_types = Table(
|
|
109
|
+
"types",
|
|
110
|
+
ischema,
|
|
111
|
+
Column("name", CoerceUnicode, key="name"),
|
|
112
|
+
Column("system_type_id", Integer, key="system_type_id"),
|
|
113
|
+
Column("user_type_id", Integer, key="user_type_id"),
|
|
114
|
+
Column("schema_id", Integer, key="schema_id"),
|
|
115
|
+
Column("max_length", Integer, key="max_length"),
|
|
116
|
+
Column("precision", Integer, key="precision"),
|
|
117
|
+
Column("scale", Integer, key="scale"),
|
|
118
|
+
Column("collation_name", CoerceUnicode, key="collation_name"),
|
|
119
|
+
Column("is_nullable", Boolean, key="is_nullable"),
|
|
120
|
+
Column("is_user_defined", Boolean, key="is_user_defined"),
|
|
121
|
+
Column("is_assembly_type", Boolean, key="is_assembly_type"),
|
|
122
|
+
Column("default_object_id", Integer, key="default_object_id"),
|
|
123
|
+
Column("rule_object_id", Integer, key="rule_object_id"),
|
|
124
|
+
Column("is_table_type", Boolean, key="is_table_type"),
|
|
125
|
+
schema="sys",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
constraints = Table(
|
|
129
|
+
"TABLE_CONSTRAINTS",
|
|
130
|
+
ischema,
|
|
131
|
+
Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
|
|
132
|
+
Column("TABLE_NAME", CoerceUnicode, key="table_name"),
|
|
133
|
+
Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"),
|
|
134
|
+
Column("CONSTRAINT_TYPE", CoerceUnicode, key="constraint_type"),
|
|
135
|
+
schema="INFORMATION_SCHEMA",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
sys_default_constraints = Table(
|
|
139
|
+
"default_constraints",
|
|
140
|
+
ischema,
|
|
141
|
+
Column("object_id", Integer),
|
|
142
|
+
Column("name", CoerceUnicode),
|
|
143
|
+
Column("schema_id", Integer),
|
|
144
|
+
Column("parent_column_id", Integer),
|
|
145
|
+
Column("definition", CoerceUnicode),
|
|
146
|
+
schema="sys",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
column_constraints = Table(
|
|
150
|
+
"CONSTRAINT_COLUMN_USAGE",
|
|
151
|
+
ischema,
|
|
152
|
+
Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
|
|
153
|
+
Column("TABLE_NAME", CoerceUnicode, key="table_name"),
|
|
154
|
+
Column("COLUMN_NAME", CoerceUnicode, key="column_name"),
|
|
155
|
+
Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"),
|
|
156
|
+
schema="INFORMATION_SCHEMA",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
key_constraints = Table(
|
|
160
|
+
"KEY_COLUMN_USAGE",
|
|
161
|
+
ischema,
|
|
162
|
+
Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
|
|
163
|
+
Column("TABLE_NAME", CoerceUnicode, key="table_name"),
|
|
164
|
+
Column("COLUMN_NAME", CoerceUnicode, key="column_name"),
|
|
165
|
+
Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"),
|
|
166
|
+
Column("CONSTRAINT_SCHEMA", CoerceUnicode, key="constraint_schema"),
|
|
167
|
+
Column("ORDINAL_POSITION", Integer, key="ordinal_position"),
|
|
168
|
+
schema="INFORMATION_SCHEMA",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
ref_constraints = Table(
|
|
172
|
+
"REFERENTIAL_CONSTRAINTS",
|
|
173
|
+
ischema,
|
|
174
|
+
Column("CONSTRAINT_CATALOG", CoerceUnicode, key="constraint_catalog"),
|
|
175
|
+
Column("CONSTRAINT_SCHEMA", CoerceUnicode, key="constraint_schema"),
|
|
176
|
+
Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"),
|
|
177
|
+
# TODO: is CATLOG misspelled ?
|
|
178
|
+
Column(
|
|
179
|
+
"UNIQUE_CONSTRAINT_CATLOG",
|
|
180
|
+
CoerceUnicode,
|
|
181
|
+
key="unique_constraint_catalog",
|
|
182
|
+
),
|
|
183
|
+
Column(
|
|
184
|
+
"UNIQUE_CONSTRAINT_SCHEMA",
|
|
185
|
+
CoerceUnicode,
|
|
186
|
+
key="unique_constraint_schema",
|
|
187
|
+
),
|
|
188
|
+
Column(
|
|
189
|
+
"UNIQUE_CONSTRAINT_NAME", CoerceUnicode, key="unique_constraint_name"
|
|
190
|
+
),
|
|
191
|
+
Column("MATCH_OPTION", String, key="match_option"),
|
|
192
|
+
Column("UPDATE_RULE", String, key="update_rule"),
|
|
193
|
+
Column("DELETE_RULE", String, key="delete_rule"),
|
|
194
|
+
schema="INFORMATION_SCHEMA",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
views = Table(
|
|
198
|
+
"VIEWS",
|
|
199
|
+
ischema,
|
|
200
|
+
Column("TABLE_CATALOG", CoerceUnicode, key="table_catalog"),
|
|
201
|
+
Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
|
|
202
|
+
Column("TABLE_NAME", CoerceUnicode, key="table_name"),
|
|
203
|
+
Column("VIEW_DEFINITION", CoerceUnicode, key="view_definition"),
|
|
204
|
+
Column("CHECK_OPTION", String, key="check_option"),
|
|
205
|
+
Column("IS_UPDATABLE", String, key="is_updatable"),
|
|
206
|
+
schema="INFORMATION_SCHEMA",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
computed_columns = Table(
|
|
210
|
+
"computed_columns",
|
|
211
|
+
ischema,
|
|
212
|
+
Column("object_id", Integer),
|
|
213
|
+
Column("name", CoerceUnicode),
|
|
214
|
+
Column("column_id", Integer),
|
|
215
|
+
Column("is_computed", Boolean),
|
|
216
|
+
Column("is_persisted", Boolean),
|
|
217
|
+
Column("definition", CoerceUnicode),
|
|
218
|
+
schema="sys",
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
sequences = Table(
|
|
222
|
+
"SEQUENCES",
|
|
223
|
+
ischema,
|
|
224
|
+
Column("SEQUENCE_CATALOG", CoerceUnicode, key="sequence_catalog"),
|
|
225
|
+
Column("SEQUENCE_SCHEMA", CoerceUnicode, key="sequence_schema"),
|
|
226
|
+
Column("SEQUENCE_NAME", CoerceUnicode, key="sequence_name"),
|
|
227
|
+
schema="INFORMATION_SCHEMA",
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class NumericSqlVariant(TypeDecorator):
|
|
232
|
+
r"""This type casts sql_variant columns in the identity_columns view
|
|
233
|
+
to numeric. This is required because:
|
|
234
|
+
|
|
235
|
+
* pyodbc does not support sql_variant
|
|
236
|
+
* pymssql under python 2 return the byte representation of the number,
|
|
237
|
+
int 1 is returned as "\x01\x00\x00\x00". On python 3 it returns the
|
|
238
|
+
correct value as string.
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
impl = Unicode
|
|
242
|
+
cache_ok = True
|
|
243
|
+
|
|
244
|
+
def column_expression(self, colexpr):
|
|
245
|
+
return cast(colexpr, Numeric(38, 0))
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
identity_columns = Table(
|
|
249
|
+
"identity_columns",
|
|
250
|
+
ischema,
|
|
251
|
+
Column("object_id", Integer),
|
|
252
|
+
Column("name", CoerceUnicode),
|
|
253
|
+
Column("column_id", Integer),
|
|
254
|
+
Column("is_identity", Boolean),
|
|
255
|
+
Column("seed_value", NumericSqlVariant),
|
|
256
|
+
Column("increment_value", NumericSqlVariant),
|
|
257
|
+
Column("last_value", NumericSqlVariant),
|
|
258
|
+
Column("is_not_for_replication", Boolean),
|
|
259
|
+
schema="sys",
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class NVarcharSqlVariant(TypeDecorator):
|
|
264
|
+
"""This type casts sql_variant columns in the extended_properties view
|
|
265
|
+
to nvarchar. This is required because pyodbc does not support sql_variant
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
impl = Unicode
|
|
269
|
+
cache_ok = True
|
|
270
|
+
|
|
271
|
+
def column_expression(self, colexpr):
|
|
272
|
+
return cast(colexpr, NVARCHAR)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
extended_properties = Table(
|
|
276
|
+
"extended_properties",
|
|
277
|
+
ischema,
|
|
278
|
+
Column("class", Integer), # TINYINT
|
|
279
|
+
Column("class_desc", CoerceUnicode),
|
|
280
|
+
Column("major_id", Integer),
|
|
281
|
+
Column("minor_id", Integer),
|
|
282
|
+
Column("name", CoerceUnicode),
|
|
283
|
+
Column("value", NVarcharSqlVariant),
|
|
284
|
+
schema="sys",
|
|
285
|
+
)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# dialects/mssql/json.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 __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from ... import types as sqltypes
|
|
13
|
+
from ...sql.sqltypes import _T_JSON
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from ...engine.interfaces import Dialect
|
|
17
|
+
from ...sql.type_api import _BindProcessorType
|
|
18
|
+
from ...sql.type_api import _LiteralProcessorType
|
|
19
|
+
|
|
20
|
+
# technically, all the dialect-specific datatypes that don't have any special
|
|
21
|
+
# behaviors would be private with names like _MSJson. However, we haven't been
|
|
22
|
+
# doing this for mysql.JSON or sqlite.JSON which both have JSON / JSONIndexType
|
|
23
|
+
# / JSONPathType in their json.py files, so keep consistent with that
|
|
24
|
+
# sub-convention for now. A future change can update them all to be
|
|
25
|
+
# package-private at once.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class JSON(sqltypes.JSON[_T_JSON]):
|
|
29
|
+
"""MSSQL JSON type.
|
|
30
|
+
|
|
31
|
+
MSSQL supports JSON-formatted data as of SQL Server 2016.
|
|
32
|
+
|
|
33
|
+
The :class:`_mssql.JSON` datatype at the DDL level will represent the
|
|
34
|
+
datatype as ``NVARCHAR(max)``, but provides for JSON-level comparison
|
|
35
|
+
functions as well as Python coercion behavior.
|
|
36
|
+
|
|
37
|
+
:class:`_mssql.JSON` is used automatically whenever the base
|
|
38
|
+
:class:`_types.JSON` datatype is used against a SQL Server backend.
|
|
39
|
+
|
|
40
|
+
.. seealso::
|
|
41
|
+
|
|
42
|
+
:class:`_types.JSON` - main documentation for the generic
|
|
43
|
+
cross-platform JSON datatype.
|
|
44
|
+
|
|
45
|
+
The :class:`_mssql.JSON` type supports persistence of JSON values
|
|
46
|
+
as well as the core index operations provided by :class:`_types.JSON`
|
|
47
|
+
datatype, by adapting the operations to render the ``JSON_VALUE``
|
|
48
|
+
or ``JSON_QUERY`` functions at the database level.
|
|
49
|
+
|
|
50
|
+
The SQL Server :class:`_mssql.JSON` type necessarily makes use of the
|
|
51
|
+
``JSON_QUERY`` and ``JSON_VALUE`` functions when querying for elements
|
|
52
|
+
of a JSON object. These two functions have a major restriction in that
|
|
53
|
+
they are **mutually exclusive** based on the type of object to be returned.
|
|
54
|
+
The ``JSON_QUERY`` function **only** returns a JSON dictionary or list,
|
|
55
|
+
but not an individual string, numeric, or boolean element; the
|
|
56
|
+
``JSON_VALUE`` function **only** returns an individual string, numeric,
|
|
57
|
+
or boolean element. **both functions either return NULL or raise
|
|
58
|
+
an error if they are not used against the correct expected value**.
|
|
59
|
+
|
|
60
|
+
To handle this awkward requirement, indexed access rules are as follows:
|
|
61
|
+
|
|
62
|
+
1. When extracting a sub element from a JSON that is itself a JSON
|
|
63
|
+
dictionary or list, the :meth:`_types.JSON.Comparator.as_json` accessor
|
|
64
|
+
should be used::
|
|
65
|
+
|
|
66
|
+
stmt = select(data_table.c.data["some key"].as_json()).where(
|
|
67
|
+
data_table.c.data["some key"].as_json() == {"sub": "structure"}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
2. When extracting a sub element from a JSON that is a plain boolean,
|
|
71
|
+
string, integer, or float, use the appropriate method among
|
|
72
|
+
:meth:`_types.JSON.Comparator.as_boolean`,
|
|
73
|
+
:meth:`_types.JSON.Comparator.as_string`,
|
|
74
|
+
:meth:`_types.JSON.Comparator.as_integer`,
|
|
75
|
+
:meth:`_types.JSON.Comparator.as_float`::
|
|
76
|
+
|
|
77
|
+
stmt = select(data_table.c.data["some key"].as_string()).where(
|
|
78
|
+
data_table.c.data["some key"].as_string() == "some string"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
.. versionadded:: 1.4
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
# note there was a result processor here that was looking for "number",
|
|
87
|
+
# but none of the tests seem to exercise it.
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Note: these objects currently match exactly those of MySQL, however since
|
|
91
|
+
# these are not generalizable to all JSON implementations, remain separately
|
|
92
|
+
# implemented for each dialect.
|
|
93
|
+
class _FormatTypeMixin:
|
|
94
|
+
def _format_value(self, value: Any) -> str:
|
|
95
|
+
raise NotImplementedError()
|
|
96
|
+
|
|
97
|
+
def bind_processor(self, dialect: Dialect) -> _BindProcessorType[Any]:
|
|
98
|
+
super_proc = self.string_bind_processor(dialect) # type: ignore[attr-defined] # noqa: E501
|
|
99
|
+
|
|
100
|
+
def process(value: Any) -> Any:
|
|
101
|
+
value = self._format_value(value)
|
|
102
|
+
if super_proc:
|
|
103
|
+
value = super_proc(value)
|
|
104
|
+
return value
|
|
105
|
+
|
|
106
|
+
return process
|
|
107
|
+
|
|
108
|
+
def literal_processor(
|
|
109
|
+
self, dialect: Dialect
|
|
110
|
+
) -> _LiteralProcessorType[Any]:
|
|
111
|
+
super_proc = self.string_literal_processor(dialect) # type: ignore[attr-defined] # noqa: E501
|
|
112
|
+
|
|
113
|
+
def process(value: Any) -> str:
|
|
114
|
+
value = self._format_value(value)
|
|
115
|
+
if super_proc:
|
|
116
|
+
value = super_proc(value)
|
|
117
|
+
return value # type: ignore[no-any-return]
|
|
118
|
+
|
|
119
|
+
return process
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class JSONIndexType(_FormatTypeMixin, sqltypes.JSON.JSONIndexType):
|
|
123
|
+
def _format_value(self, value: Any) -> str:
|
|
124
|
+
if isinstance(value, int):
|
|
125
|
+
formatted_value = "$[%s]" % value
|
|
126
|
+
else:
|
|
127
|
+
formatted_value = '$."%s"' % value
|
|
128
|
+
return formatted_value
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class JSONPathType(_FormatTypeMixin, sqltypes.JSON.JSONPathType):
|
|
132
|
+
def _format_value(self, value: Any) -> str:
|
|
133
|
+
return "$%s" % (
|
|
134
|
+
"".join(
|
|
135
|
+
[
|
|
136
|
+
"[%s]" % elem if isinstance(elem, int) else '."%s"' % elem
|
|
137
|
+
for elem in value
|
|
138
|
+
]
|
|
139
|
+
)
|
|
140
|
+
)
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# dialects/mssql/mssqlpython.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
|
+
.. dialect:: mssql+mssqlpython
|
|
11
|
+
:name: mssqlpython
|
|
12
|
+
:dbapi: mssql-python
|
|
13
|
+
:connectstring: mssql+mssqlpython://<username>:<password>@<host>:<port>/<dbname>
|
|
14
|
+
:url: https://github.com/microsoft/mssql-python
|
|
15
|
+
|
|
16
|
+
mssql-python is a driver for Microsoft SQL Server produced by Microsoft.
|
|
17
|
+
|
|
18
|
+
.. versionadded:: 2.1.0b2
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
The driver is generally similar to pyodbc in most aspects as it is based
|
|
22
|
+
on the same ODBC framework.
|
|
23
|
+
|
|
24
|
+
Connection Strings
|
|
25
|
+
------------------
|
|
26
|
+
|
|
27
|
+
Examples of connecting with the mssql-python driver::
|
|
28
|
+
|
|
29
|
+
from sqlalchemy import create_engine
|
|
30
|
+
|
|
31
|
+
# Basic connection
|
|
32
|
+
engine = create_engine(
|
|
33
|
+
"mssql+mssqlpython://user:password@hostname/database"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# With Windows Authentication
|
|
37
|
+
engine = create_engine(
|
|
38
|
+
"mssql+mssqlpython://hostname/database?authentication=ActiveDirectoryIntegrated"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
""" # noqa
|
|
42
|
+
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
import re
|
|
46
|
+
from typing import Any
|
|
47
|
+
from typing import Dict
|
|
48
|
+
from typing import List
|
|
49
|
+
from typing import Optional
|
|
50
|
+
from typing import TYPE_CHECKING
|
|
51
|
+
from typing import Union
|
|
52
|
+
|
|
53
|
+
from .base import MSDialect
|
|
54
|
+
from .pyodbc import _ms_numeric_pyodbc
|
|
55
|
+
from ... import util
|
|
56
|
+
from ...sql import sqltypes
|
|
57
|
+
|
|
58
|
+
if TYPE_CHECKING:
|
|
59
|
+
from ... import pool
|
|
60
|
+
from ...engine import interfaces
|
|
61
|
+
from ...engine.interfaces import ConnectArgsType
|
|
62
|
+
from ...engine.interfaces import DBAPIModule
|
|
63
|
+
from ...engine.interfaces import IsolationLevel
|
|
64
|
+
from ...engine.interfaces import URL
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class _MSNumeric_mssqlpython(_ms_numeric_pyodbc, sqltypes.Numeric):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class _MSFloat_mssqlpython(_ms_numeric_pyodbc, sqltypes.Float):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class MSDialect_mssqlpython(MSDialect):
|
|
76
|
+
driver = "mssqlpython"
|
|
77
|
+
supports_statement_cache = True
|
|
78
|
+
|
|
79
|
+
supports_sane_rowcount_returning = True
|
|
80
|
+
supports_sane_multi_rowcount = True
|
|
81
|
+
supports_native_uuid = True
|
|
82
|
+
scope_identity_must_be_embedded = True
|
|
83
|
+
|
|
84
|
+
supports_native_decimal = True
|
|
85
|
+
|
|
86
|
+
# used by pyodbc _ms_numeric_pyodbc class
|
|
87
|
+
_need_decimal_fix = True
|
|
88
|
+
|
|
89
|
+
colspecs = util.update_copy(
|
|
90
|
+
MSDialect.colspecs,
|
|
91
|
+
{
|
|
92
|
+
sqltypes.Numeric: _MSNumeric_mssqlpython,
|
|
93
|
+
sqltypes.Float: _MSFloat_mssqlpython,
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def __init__(self, enable_pooling=False, **kw):
|
|
98
|
+
super().__init__(**kw)
|
|
99
|
+
if not enable_pooling and self.dbapi is not None:
|
|
100
|
+
self.loaded_dbapi.pooling(enabled=False)
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def import_dbapi(cls) -> DBAPIModule:
|
|
104
|
+
return __import__("mssql_python")
|
|
105
|
+
|
|
106
|
+
def create_connect_args(self, url: URL) -> ConnectArgsType:
|
|
107
|
+
opts = url.translate_connect_args(username="user")
|
|
108
|
+
opts.update(url.query)
|
|
109
|
+
|
|
110
|
+
keys = opts
|
|
111
|
+
|
|
112
|
+
query = url.query
|
|
113
|
+
|
|
114
|
+
connect_args: Dict[str, Any] = {}
|
|
115
|
+
connectors: List[str]
|
|
116
|
+
|
|
117
|
+
def check_quote(token: str) -> str:
|
|
118
|
+
if ";" in str(token) or str(token).startswith("{"):
|
|
119
|
+
token = "{%s}" % token.replace("}", "}}")
|
|
120
|
+
return token
|
|
121
|
+
|
|
122
|
+
keys = {k: check_quote(v) for k, v in keys.items()}
|
|
123
|
+
|
|
124
|
+
port = ""
|
|
125
|
+
if "port" in keys and "port" not in query:
|
|
126
|
+
port = ",%d" % int(keys.pop("port"))
|
|
127
|
+
|
|
128
|
+
connectors = []
|
|
129
|
+
|
|
130
|
+
connectors.extend(
|
|
131
|
+
[
|
|
132
|
+
"Server=%s%s" % (keys.pop("host", ""), port),
|
|
133
|
+
"Database=%s" % keys.pop("database", ""),
|
|
134
|
+
]
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
user = keys.pop("user", None)
|
|
138
|
+
if user:
|
|
139
|
+
connectors.append("UID=%s" % user)
|
|
140
|
+
pwd = keys.pop("password", "")
|
|
141
|
+
if pwd:
|
|
142
|
+
connectors.append("PWD=%s" % pwd)
|
|
143
|
+
else:
|
|
144
|
+
authentication = keys.pop("authentication", None)
|
|
145
|
+
if authentication:
|
|
146
|
+
connectors.append("Authentication=%s" % authentication)
|
|
147
|
+
|
|
148
|
+
connectors.extend(["%s=%s" % (k, v) for k, v in keys.items()])
|
|
149
|
+
|
|
150
|
+
return ((";".join(connectors),), connect_args)
|
|
151
|
+
|
|
152
|
+
def is_disconnect(
|
|
153
|
+
self,
|
|
154
|
+
e: Exception,
|
|
155
|
+
connection: Optional[
|
|
156
|
+
Union[pool.PoolProxiedConnection, interfaces.DBAPIConnection]
|
|
157
|
+
],
|
|
158
|
+
cursor: Optional[interfaces.DBAPICursor],
|
|
159
|
+
) -> bool:
|
|
160
|
+
if isinstance(e, self.loaded_dbapi.ProgrammingError):
|
|
161
|
+
return (
|
|
162
|
+
"The cursor's connection has been closed." in str(e)
|
|
163
|
+
or "Attempt to use a closed connection." in str(e)
|
|
164
|
+
or "Driver Error: Operation cannot be performed" in str(e)
|
|
165
|
+
)
|
|
166
|
+
elif isinstance(e, self.loaded_dbapi.InterfaceError):
|
|
167
|
+
return bool(re.search(r"Cannot .* on closed connection", str(e)))
|
|
168
|
+
else:
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
def _dbapi_version(self) -> interfaces.VersionInfoType:
|
|
172
|
+
if not self.dbapi:
|
|
173
|
+
return ()
|
|
174
|
+
return self._parse_dbapi_version(self.dbapi.version)
|
|
175
|
+
|
|
176
|
+
def _parse_dbapi_version(self, vers: str) -> interfaces.VersionInfoType:
|
|
177
|
+
m = re.match(r"(?:py.*-)?([\d\.]+)(?:-(\w+))?", vers)
|
|
178
|
+
if not m:
|
|
179
|
+
return ()
|
|
180
|
+
vers_tuple: interfaces.VersionInfoType = tuple(
|
|
181
|
+
[int(x) for x in m.group(1).split(".")]
|
|
182
|
+
)
|
|
183
|
+
if m.group(2):
|
|
184
|
+
vers_tuple += (m.group(2),)
|
|
185
|
+
return vers_tuple
|
|
186
|
+
|
|
187
|
+
def _get_server_version_info(self, connection):
|
|
188
|
+
vers = connection.exec_driver_sql("select @@version").scalar()
|
|
189
|
+
m = re.match(r"Microsoft .*? - (\d+)\.(\d+)\.(\d+)\.(\d+)", vers)
|
|
190
|
+
if m:
|
|
191
|
+
return tuple(int(x) for x in m.group(1, 2, 3, 4))
|
|
192
|
+
else:
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
def get_isolation_level_values(
|
|
196
|
+
self, dbapi_connection: interfaces.DBAPIConnection
|
|
197
|
+
) -> List[IsolationLevel]:
|
|
198
|
+
return [
|
|
199
|
+
*super().get_isolation_level_values(dbapi_connection),
|
|
200
|
+
"AUTOCOMMIT",
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
def set_isolation_level(
|
|
204
|
+
self,
|
|
205
|
+
dbapi_connection: interfaces.DBAPIConnection,
|
|
206
|
+
level: IsolationLevel,
|
|
207
|
+
) -> None:
|
|
208
|
+
if level == "AUTOCOMMIT":
|
|
209
|
+
dbapi_connection.autocommit = True
|
|
210
|
+
else:
|
|
211
|
+
dbapi_connection.autocommit = False
|
|
212
|
+
super().set_isolation_level(dbapi_connection, level)
|
|
213
|
+
|
|
214
|
+
def detect_autocommit_setting(
|
|
215
|
+
self, dbapi_conn: interfaces.DBAPIConnection
|
|
216
|
+
) -> bool:
|
|
217
|
+
return bool(dbapi_conn.autocommit)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
dialect = MSDialect_mssqlpython
|