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,196 @@
|
|
|
1
|
+
# dialects/mssql/provision.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 sqlalchemy import inspect
|
|
10
|
+
from sqlalchemy import Integer
|
|
11
|
+
from ... import create_engine
|
|
12
|
+
from ... import exc
|
|
13
|
+
from ...schema import Column
|
|
14
|
+
from ...schema import DropConstraint
|
|
15
|
+
from ...schema import ForeignKeyConstraint
|
|
16
|
+
from ...schema import MetaData
|
|
17
|
+
from ...schema import Table
|
|
18
|
+
from ...testing.provision import create_db
|
|
19
|
+
from ...testing.provision import dbapi_error
|
|
20
|
+
from ...testing.provision import drop_all_schema_objects_pre_tables
|
|
21
|
+
from ...testing.provision import drop_db
|
|
22
|
+
from ...testing.provision import generate_driver_url
|
|
23
|
+
from ...testing.provision import get_temp_table_name
|
|
24
|
+
from ...testing.provision import log
|
|
25
|
+
from ...testing.provision import normalize_sequence
|
|
26
|
+
from ...testing.provision import post_configure_engine
|
|
27
|
+
from ...testing.provision import run_reap_dbs
|
|
28
|
+
from ...testing.provision import temp_table_keyword_args
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@post_configure_engine.for_db("mssql")
|
|
32
|
+
def post_configure_engine(url, engine, follower_ident):
|
|
33
|
+
if engine.driver == "pyodbc":
|
|
34
|
+
engine.dialect.dbapi.pooling = False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@generate_driver_url.for_db("mssql")
|
|
38
|
+
def generate_driver_url(url, driver, query_str):
|
|
39
|
+
backend = url.get_backend_name()
|
|
40
|
+
|
|
41
|
+
new_url = url.set(drivername="%s+%s" % (backend, driver))
|
|
42
|
+
|
|
43
|
+
if driver == "pymssql" and url.get_driver_name() != "pymssql":
|
|
44
|
+
new_url = new_url.set(query="")
|
|
45
|
+
elif driver == "mssqlpython" and url.get_driver_name() != "mssqlpython":
|
|
46
|
+
new_url = new_url.set(query={"Encrypt": "No"})
|
|
47
|
+
|
|
48
|
+
if driver == "aioodbc":
|
|
49
|
+
new_url = new_url.update_query_dict({"MARS_Connection": "Yes"})
|
|
50
|
+
|
|
51
|
+
if query_str:
|
|
52
|
+
new_url = new_url.update_query_string(query_str)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
new_url.get_dialect()
|
|
56
|
+
except exc.NoSuchModuleError:
|
|
57
|
+
return None
|
|
58
|
+
else:
|
|
59
|
+
return new_url
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@create_db.for_db("mssql")
|
|
63
|
+
def _mssql_create_db(cfg, eng, ident):
|
|
64
|
+
with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
|
|
65
|
+
conn.exec_driver_sql("create database %s" % ident)
|
|
66
|
+
conn.exec_driver_sql(
|
|
67
|
+
"ALTER DATABASE %s SET ALLOW_SNAPSHOT_ISOLATION ON" % ident
|
|
68
|
+
)
|
|
69
|
+
conn.exec_driver_sql(
|
|
70
|
+
"ALTER DATABASE %s SET READ_COMMITTED_SNAPSHOT ON" % ident
|
|
71
|
+
)
|
|
72
|
+
conn.exec_driver_sql("use %s" % ident)
|
|
73
|
+
conn.exec_driver_sql("create schema test_schema")
|
|
74
|
+
conn.exec_driver_sql("create schema test_schema_2")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@drop_db.for_db("mssql")
|
|
78
|
+
def _mssql_drop_db(cfg, eng, ident):
|
|
79
|
+
with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
|
|
80
|
+
_mssql_drop_ignore(conn, ident)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _mssql_drop_ignore(conn, ident):
|
|
84
|
+
try:
|
|
85
|
+
# typically when this happens, we can't KILL the session anyway,
|
|
86
|
+
# so let the cleanup process drop the DBs
|
|
87
|
+
# for row in conn.exec_driver_sql(
|
|
88
|
+
# "select session_id from sys.dm_exec_sessions "
|
|
89
|
+
# "where database_id=db_id('%s')" % ident):
|
|
90
|
+
# log.info("killing SQL server session %s", row['session_id'])
|
|
91
|
+
# conn.exec_driver_sql("kill %s" % row['session_id'])
|
|
92
|
+
conn.exec_driver_sql("drop database %s" % ident)
|
|
93
|
+
log.info("Reaped db: %s", ident)
|
|
94
|
+
return True
|
|
95
|
+
except exc.DatabaseError as err:
|
|
96
|
+
log.warning("couldn't drop db: %s", err)
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@run_reap_dbs.for_db("mssql")
|
|
101
|
+
def _reap_mssql_dbs(url, idents):
|
|
102
|
+
log.info("db reaper connecting to %r", url)
|
|
103
|
+
eng = create_engine(url)
|
|
104
|
+
with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
|
|
105
|
+
log.info("identifiers in file: %s", ", ".join(idents))
|
|
106
|
+
|
|
107
|
+
to_reap = conn.exec_driver_sql(
|
|
108
|
+
"select d.name from sys.databases as d where name "
|
|
109
|
+
"like 'TEST_%' and not exists (select session_id "
|
|
110
|
+
"from sys.dm_exec_sessions "
|
|
111
|
+
"where database_id=d.database_id)"
|
|
112
|
+
)
|
|
113
|
+
all_names = {dbname.lower() for (dbname,) in to_reap}
|
|
114
|
+
to_drop = set()
|
|
115
|
+
for name in all_names:
|
|
116
|
+
if name in idents:
|
|
117
|
+
to_drop.add(name)
|
|
118
|
+
|
|
119
|
+
dropped = total = 0
|
|
120
|
+
for total, dbname in enumerate(to_drop, 1):
|
|
121
|
+
if _mssql_drop_ignore(conn, dbname):
|
|
122
|
+
dropped += 1
|
|
123
|
+
log.info(
|
|
124
|
+
"Dropped %d out of %d stale databases detected", dropped, total
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@temp_table_keyword_args.for_db("mssql")
|
|
129
|
+
def _mssql_temp_table_keyword_args(cfg, eng):
|
|
130
|
+
return {}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@get_temp_table_name.for_db("mssql")
|
|
134
|
+
def _mssql_get_temp_table_name(cfg, eng, base_name):
|
|
135
|
+
return "##" + base_name
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@drop_all_schema_objects_pre_tables.for_db("mssql")
|
|
139
|
+
def drop_all_schema_objects_pre_tables(cfg, eng):
|
|
140
|
+
with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
|
|
141
|
+
inspector = inspect(conn)
|
|
142
|
+
|
|
143
|
+
# Drop all full-text indexes before dropping catalogs
|
|
144
|
+
fulltext_indexes = conn.exec_driver_sql(
|
|
145
|
+
"SELECT OBJECT_SCHEMA_NAME(object_id) AS schema_name, "
|
|
146
|
+
"OBJECT_NAME(object_id) AS table_name "
|
|
147
|
+
"FROM sys.fulltext_indexes"
|
|
148
|
+
).fetchall()
|
|
149
|
+
|
|
150
|
+
for schema_name, table_name in fulltext_indexes:
|
|
151
|
+
if schema_name:
|
|
152
|
+
qualified_name = f"[{schema_name}].[{table_name}]"
|
|
153
|
+
else:
|
|
154
|
+
qualified_name = f"[{table_name}]"
|
|
155
|
+
conn.exec_driver_sql(f"DROP FULLTEXT INDEX ON {qualified_name}")
|
|
156
|
+
|
|
157
|
+
# Now drop all full-text catalogs
|
|
158
|
+
fulltext_catalogs = conn.exec_driver_sql(
|
|
159
|
+
"SELECT name FROM sys.fulltext_catalogs"
|
|
160
|
+
).fetchall()
|
|
161
|
+
|
|
162
|
+
for (catalog_name,) in fulltext_catalogs:
|
|
163
|
+
conn.exec_driver_sql(f"DROP FULLTEXT CATALOG [{catalog_name}]")
|
|
164
|
+
|
|
165
|
+
for schema in (None, "dbo", cfg.test_schema, cfg.test_schema_2):
|
|
166
|
+
for tname in inspector.get_table_names(schema=schema):
|
|
167
|
+
tb = Table(
|
|
168
|
+
tname,
|
|
169
|
+
MetaData(),
|
|
170
|
+
Column("x", Integer),
|
|
171
|
+
Column("y", Integer),
|
|
172
|
+
schema=schema,
|
|
173
|
+
)
|
|
174
|
+
for fk in inspect(conn).get_foreign_keys(tname, schema=schema):
|
|
175
|
+
conn.execute(
|
|
176
|
+
DropConstraint(
|
|
177
|
+
ForeignKeyConstraint(
|
|
178
|
+
[tb.c.x], [tb.c.y], name=fk["name"]
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@normalize_sequence.for_db("mssql")
|
|
185
|
+
def normalize_sequence(cfg, sequence):
|
|
186
|
+
if sequence.start is None:
|
|
187
|
+
sequence.start = 1
|
|
188
|
+
return sequence
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@dbapi_error.for_db("mssql")
|
|
192
|
+
def dbapi_error(cfg, cls, message):
|
|
193
|
+
if cfg.db.driver == "mssqlpython":
|
|
194
|
+
return cls(message, "placeholder for mssqlpython")
|
|
195
|
+
else:
|
|
196
|
+
return cls(message)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# dialects/mssql/pymssql.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
|
+
"""
|
|
11
|
+
.. dialect:: mssql+pymssql
|
|
12
|
+
:name: pymssql
|
|
13
|
+
:dbapi: pymssql
|
|
14
|
+
:connectstring: mssql+pymssql://<username>:<password>@<freetds_name>/?charset=utf8
|
|
15
|
+
|
|
16
|
+
pymssql is a Python module that provides a Python DBAPI interface around
|
|
17
|
+
`FreeTDS <https://www.freetds.org/>`_.
|
|
18
|
+
|
|
19
|
+
.. versionchanged:: 2.0.5
|
|
20
|
+
|
|
21
|
+
pymssql was restored to SQLAlchemy's continuous integration testing
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
""" # noqa
|
|
25
|
+
import re
|
|
26
|
+
|
|
27
|
+
from .base import MSDialect
|
|
28
|
+
from .base import MSIdentifierPreparer
|
|
29
|
+
from ... import types as sqltypes
|
|
30
|
+
from ... import util
|
|
31
|
+
from ...engine import processors
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _MSNumeric_pymssql(sqltypes.Numeric):
|
|
35
|
+
def result_processor(self, dialect, type_):
|
|
36
|
+
if not self.asdecimal:
|
|
37
|
+
return processors.to_float
|
|
38
|
+
else:
|
|
39
|
+
return sqltypes.Numeric.result_processor(self, dialect, type_)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class MSIdentifierPreparer_pymssql(MSIdentifierPreparer):
|
|
43
|
+
def __init__(self, dialect):
|
|
44
|
+
super().__init__(dialect)
|
|
45
|
+
# pymssql has the very unusual behavior that it uses pyformat
|
|
46
|
+
# yet does not require that percent signs be doubled
|
|
47
|
+
self._double_percents = False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class MSDialect_pymssql(MSDialect):
|
|
51
|
+
supports_statement_cache = True
|
|
52
|
+
supports_native_decimal = True
|
|
53
|
+
supports_native_uuid = True
|
|
54
|
+
driver = "pymssql"
|
|
55
|
+
|
|
56
|
+
preparer = MSIdentifierPreparer_pymssql
|
|
57
|
+
|
|
58
|
+
colspecs = util.update_copy(
|
|
59
|
+
MSDialect.colspecs,
|
|
60
|
+
{sqltypes.Numeric: _MSNumeric_pymssql, sqltypes.Float: sqltypes.Float},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def import_dbapi(cls):
|
|
65
|
+
module = __import__("pymssql")
|
|
66
|
+
# pymmsql < 2.1.1 doesn't have a Binary method. we use string
|
|
67
|
+
client_ver = tuple(int(x) for x in module.__version__.split("."))
|
|
68
|
+
if client_ver < (2, 1, 1):
|
|
69
|
+
# TODO: monkeypatching here is less than ideal
|
|
70
|
+
module.Binary = lambda x: x if hasattr(x, "decode") else str(x)
|
|
71
|
+
|
|
72
|
+
if client_ver < (1,):
|
|
73
|
+
util.warn(
|
|
74
|
+
"The pymssql dialect expects at least "
|
|
75
|
+
"the 1.0 series of the pymssql DBAPI."
|
|
76
|
+
)
|
|
77
|
+
return module
|
|
78
|
+
|
|
79
|
+
def _get_server_version_info(self, connection):
|
|
80
|
+
vers = connection.exec_driver_sql("select @@version").scalar()
|
|
81
|
+
m = re.match(r"Microsoft .*? - (\d+)\.(\d+)\.(\d+)\.(\d+)", vers)
|
|
82
|
+
if m:
|
|
83
|
+
return tuple(int(x) for x in m.group(1, 2, 3, 4))
|
|
84
|
+
else:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
def create_connect_args(self, url):
|
|
88
|
+
opts = url.translate_connect_args(username="user")
|
|
89
|
+
opts.update(url.query)
|
|
90
|
+
port = opts.pop("port", None)
|
|
91
|
+
if port and "host" in opts:
|
|
92
|
+
opts["host"] = "%s:%s" % (opts["host"], port)
|
|
93
|
+
return ([], opts)
|
|
94
|
+
|
|
95
|
+
def is_disconnect(self, e, connection, cursor):
|
|
96
|
+
for msg in (
|
|
97
|
+
"Adaptive Server connection timed out",
|
|
98
|
+
"Net-Lib error during Connection reset by peer",
|
|
99
|
+
"message 20003", # connection timeout
|
|
100
|
+
"Error 10054",
|
|
101
|
+
"Not connected to any MS SQL server",
|
|
102
|
+
"Connection is closed",
|
|
103
|
+
"message 20006", # Write to the server failed
|
|
104
|
+
"message 20017", # Unexpected EOF from the server
|
|
105
|
+
"message 20047", # DBPROCESS is dead or not enabled
|
|
106
|
+
"The server failed to resume the transaction",
|
|
107
|
+
):
|
|
108
|
+
if msg in str(e):
|
|
109
|
+
return True
|
|
110
|
+
else:
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
def get_isolation_level_values(self, dbapi_connection):
|
|
114
|
+
return super().get_isolation_level_values(dbapi_connection) + [
|
|
115
|
+
"AUTOCOMMIT"
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
def set_isolation_level(self, dbapi_connection, level):
|
|
119
|
+
if level == "AUTOCOMMIT":
|
|
120
|
+
dbapi_connection.autocommit(True)
|
|
121
|
+
else:
|
|
122
|
+
dbapi_connection.autocommit(False)
|
|
123
|
+
super().set_isolation_level(dbapi_connection, level)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
dialect = MSDialect_pymssql
|