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.
Files changed (270) hide show
  1. sqlalchemy/__init__.py +298 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +250 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +30 -0
  8. sqlalchemy/dialects/mssql/__init__.py +89 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4166 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +220 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +698 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +226 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +214 -0
  21. sqlalchemy/dialects/mysql/base.py +3877 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +330 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +296 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +312 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +157 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +156 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3977 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1601 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +158 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +909 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +368 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +171 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +229 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1323 -0
  50. sqlalchemy/dialects/postgresql/base.py +5789 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +593 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +423 -0
  55. sqlalchemy/dialects/postgresql/json.py +408 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +521 -0
  57. sqlalchemy/dialects/postgresql/operators.py +130 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +670 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +344 -0
  60. sqlalchemy/dialects/postgresql/provision.py +184 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +799 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +321 -0
  68. sqlalchemy/dialects/sqlite/base.py +3063 -0
  69. sqlalchemy/dialects/sqlite/dml.py +279 -0
  70. sqlalchemy/dialects/sqlite/json.py +100 -0
  71. sqlalchemy/dialects/sqlite/provision.py +229 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +161 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +754 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp313t-win_arm64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp313t-win_arm64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +633 -0
  80. sqlalchemy/engine/_row_cy.cp313t-win_arm64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp313t-win_arm64.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3354 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2421 -0
  88. sqlalchemy/engine/default.py +2402 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3495 -0
  91. sqlalchemy/engine/mock.py +134 -0
  92. sqlalchemy/engine/processors.py +82 -0
  93. sqlalchemy/engine/reflection.py +2100 -0
  94. sqlalchemy/engine/result.py +1966 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +156 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +674 -0
  102. sqlalchemy/event/base.py +472 -0
  103. sqlalchemy/event/legacy.py +258 -0
  104. sqlalchemy/event/registry.py +390 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +922 -0
  107. sqlalchemy/ext/__init__.py +11 -0
  108. sqlalchemy/ext/associationproxy.py +2072 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1487 -0
  112. sqlalchemy/ext/asyncio/exc.py +21 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1679 -0
  115. sqlalchemy/ext/asyncio/session.py +2007 -0
  116. sqlalchemy/ext/automap.py +1701 -0
  117. sqlalchemy/ext/baked.py +559 -0
  118. sqlalchemy/ext/compiler.py +600 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +560 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +439 -0
  127. sqlalchemy/ext/serializer.py +185 -0
  128. sqlalchemy/future/__init__.py +16 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +174 -0
  131. sqlalchemy/log.py +283 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +179 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +976 -0
  137. sqlalchemy/orm/bulk_persistence.py +2152 -0
  138. sqlalchemy/orm/clsregistry.py +582 -0
  139. sqlalchemy/orm/collections.py +1568 -0
  140. sqlalchemy/orm/context.py +3471 -0
  141. sqlalchemy/orm/decl_api.py +2280 -0
  142. sqlalchemy/orm/decl_base.py +2309 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +307 -0
  146. sqlalchemy/orm/evaluator.py +379 -0
  147. sqlalchemy/orm/events.py +3386 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +746 -0
  151. sqlalchemy/orm/interfaces.py +1589 -0
  152. sqlalchemy/orm/loading.py +1684 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4411 -0
  155. sqlalchemy/orm/path_registry.py +829 -0
  156. sqlalchemy/orm/persistence.py +1789 -0
  157. sqlalchemy/orm/properties.py +973 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3570 -0
  160. sqlalchemy/orm/scoping.py +2232 -0
  161. sqlalchemy/orm/session.py +5403 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3492 -0
  165. sqlalchemy/orm/strategy_options.py +2562 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +798 -0
  168. sqlalchemy/orm/util.py +2438 -0
  169. sqlalchemy/orm/writeonly.py +694 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1522 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +582 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +74 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +397 -0
  178. sqlalchemy/sql/_dml_constructors.py +132 -0
  179. sqlalchemy/sql/_elements_constructors.py +2164 -0
  180. sqlalchemy/sql/_orm_types.py +20 -0
  181. sqlalchemy/sql/_selectable_constructors.py +840 -0
  182. sqlalchemy/sql/_typing.py +487 -0
  183. sqlalchemy/sql/_util_cy.cp313t-win_arm64.pyd +0 -0
  184. sqlalchemy/sql/_util_cy.py +127 -0
  185. sqlalchemy/sql/annotation.py +590 -0
  186. sqlalchemy/sql/base.py +2699 -0
  187. sqlalchemy/sql/cache_key.py +1066 -0
  188. sqlalchemy/sql/coercions.py +1373 -0
  189. sqlalchemy/sql/compiler.py +8327 -0
  190. sqlalchemy/sql/crud.py +1815 -0
  191. sqlalchemy/sql/ddl.py +1928 -0
  192. sqlalchemy/sql/default_comparator.py +654 -0
  193. sqlalchemy/sql/dml.py +1977 -0
  194. sqlalchemy/sql/elements.py +6033 -0
  195. sqlalchemy/sql/events.py +458 -0
  196. sqlalchemy/sql/expression.py +172 -0
  197. sqlalchemy/sql/functions.py +2305 -0
  198. sqlalchemy/sql/lambdas.py +1443 -0
  199. sqlalchemy/sql/naming.py +209 -0
  200. sqlalchemy/sql/operators.py +2897 -0
  201. sqlalchemy/sql/roles.py +332 -0
  202. sqlalchemy/sql/schema.py +6703 -0
  203. sqlalchemy/sql/selectable.py +7553 -0
  204. sqlalchemy/sql/sqltypes.py +4093 -0
  205. sqlalchemy/sql/traversals.py +1042 -0
  206. sqlalchemy/sql/type_api.py +2446 -0
  207. sqlalchemy/sql/util.py +1495 -0
  208. sqlalchemy/sql/visitors.py +1157 -0
  209. sqlalchemy/testing/__init__.py +96 -0
  210. sqlalchemy/testing/assertions.py +1007 -0
  211. sqlalchemy/testing/assertsql.py +519 -0
  212. sqlalchemy/testing/asyncio.py +128 -0
  213. sqlalchemy/testing/config.py +440 -0
  214. sqlalchemy/testing/engines.py +483 -0
  215. sqlalchemy/testing/entities.py +117 -0
  216. sqlalchemy/testing/exclusions.py +476 -0
  217. sqlalchemy/testing/fixtures/__init__.py +30 -0
  218. sqlalchemy/testing/fixtures/base.py +384 -0
  219. sqlalchemy/testing/fixtures/mypy.py +247 -0
  220. sqlalchemy/testing/fixtures/orm.py +227 -0
  221. sqlalchemy/testing/fixtures/sql.py +538 -0
  222. sqlalchemy/testing/pickleable.py +155 -0
  223. sqlalchemy/testing/plugin/__init__.py +6 -0
  224. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  225. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  226. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  227. sqlalchemy/testing/profiling.py +329 -0
  228. sqlalchemy/testing/provision.py +613 -0
  229. sqlalchemy/testing/requirements.py +1978 -0
  230. sqlalchemy/testing/schema.py +198 -0
  231. sqlalchemy/testing/suite/__init__.py +19 -0
  232. sqlalchemy/testing/suite/test_cte.py +237 -0
  233. sqlalchemy/testing/suite/test_ddl.py +420 -0
  234. sqlalchemy/testing/suite/test_dialect.py +776 -0
  235. sqlalchemy/testing/suite/test_insert.py +630 -0
  236. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  237. sqlalchemy/testing/suite/test_results.py +660 -0
  238. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  239. sqlalchemy/testing/suite/test_select.py +2112 -0
  240. sqlalchemy/testing/suite/test_sequence.py +317 -0
  241. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  242. sqlalchemy/testing/suite/test_types.py +2271 -0
  243. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  244. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  245. sqlalchemy/testing/util.py +535 -0
  246. sqlalchemy/testing/warnings.py +52 -0
  247. sqlalchemy/types.py +76 -0
  248. sqlalchemy/util/__init__.py +158 -0
  249. sqlalchemy/util/_collections.py +688 -0
  250. sqlalchemy/util/_collections_cy.cp313t-win_arm64.pyd +0 -0
  251. sqlalchemy/util/_collections_cy.pxd +8 -0
  252. sqlalchemy/util/_collections_cy.py +516 -0
  253. sqlalchemy/util/_has_cython.py +46 -0
  254. sqlalchemy/util/_immutabledict_cy.cp313t-win_arm64.pyd +0 -0
  255. sqlalchemy/util/_immutabledict_cy.py +240 -0
  256. sqlalchemy/util/compat.py +299 -0
  257. sqlalchemy/util/concurrency.py +322 -0
  258. sqlalchemy/util/cython.py +79 -0
  259. sqlalchemy/util/deprecations.py +401 -0
  260. sqlalchemy/util/langhelpers.py +2320 -0
  261. sqlalchemy/util/preloaded.py +152 -0
  262. sqlalchemy/util/queue.py +304 -0
  263. sqlalchemy/util/tool_support.py +201 -0
  264. sqlalchemy/util/topological.py +120 -0
  265. sqlalchemy/util/typing.py +711 -0
  266. sqlalchemy-2.1.0b2.dist-info/METADATA +269 -0
  267. sqlalchemy-2.1.0b2.dist-info/RECORD +270 -0
  268. sqlalchemy-2.1.0b2.dist-info/WHEEL +5 -0
  269. sqlalchemy-2.1.0b2.dist-info/licenses/LICENSE +19 -0
  270. 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