SQLAlchemy 2.0.36__cp313-cp313-win32.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 (273) hide show
  1. SQLAlchemy-2.0.36.dist-info/LICENSE +19 -0
  2. SQLAlchemy-2.0.36.dist-info/METADATA +243 -0
  3. SQLAlchemy-2.0.36.dist-info/RECORD +273 -0
  4. SQLAlchemy-2.0.36.dist-info/WHEEL +5 -0
  5. SQLAlchemy-2.0.36.dist-info/top_level.txt +1 -0
  6. sqlalchemy/__init__.py +294 -0
  7. sqlalchemy/connectors/__init__.py +18 -0
  8. sqlalchemy/connectors/aioodbc.py +174 -0
  9. sqlalchemy/connectors/asyncio.py +213 -0
  10. sqlalchemy/connectors/pyodbc.py +249 -0
  11. sqlalchemy/cyextension/__init__.py +6 -0
  12. sqlalchemy/cyextension/collections.cp313-win32.pyd +0 -0
  13. sqlalchemy/cyextension/collections.pyx +409 -0
  14. sqlalchemy/cyextension/immutabledict.cp313-win32.pyd +0 -0
  15. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  16. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  17. sqlalchemy/cyextension/processors.cp313-win32.pyd +0 -0
  18. sqlalchemy/cyextension/processors.pyx +68 -0
  19. sqlalchemy/cyextension/resultproxy.cp313-win32.pyd +0 -0
  20. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  21. sqlalchemy/cyextension/util.cp313-win32.pyd +0 -0
  22. sqlalchemy/cyextension/util.pyx +91 -0
  23. sqlalchemy/dialects/__init__.py +61 -0
  24. sqlalchemy/dialects/_typing.py +25 -0
  25. sqlalchemy/dialects/mssql/__init__.py +88 -0
  26. sqlalchemy/dialects/mssql/aioodbc.py +64 -0
  27. sqlalchemy/dialects/mssql/base.py +4010 -0
  28. sqlalchemy/dialects/mssql/information_schema.py +254 -0
  29. sqlalchemy/dialects/mssql/json.py +133 -0
  30. sqlalchemy/dialects/mssql/provision.py +162 -0
  31. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  32. sqlalchemy/dialects/mssql/pyodbc.py +745 -0
  33. sqlalchemy/dialects/mysql/__init__.py +101 -0
  34. sqlalchemy/dialects/mysql/aiomysql.py +333 -0
  35. sqlalchemy/dialects/mysql/asyncmy.py +337 -0
  36. sqlalchemy/dialects/mysql/base.py +3494 -0
  37. sqlalchemy/dialects/mysql/cymysql.py +84 -0
  38. sqlalchemy/dialects/mysql/dml.py +219 -0
  39. sqlalchemy/dialects/mysql/enumerated.py +244 -0
  40. sqlalchemy/dialects/mysql/expression.py +141 -0
  41. sqlalchemy/dialects/mysql/json.py +81 -0
  42. sqlalchemy/dialects/mysql/mariadb.py +32 -0
  43. sqlalchemy/dialects/mysql/mariadbconnector.py +277 -0
  44. sqlalchemy/dialects/mysql/mysqlconnector.py +180 -0
  45. sqlalchemy/dialects/mysql/mysqldb.py +303 -0
  46. sqlalchemy/dialects/mysql/provision.py +110 -0
  47. sqlalchemy/dialects/mysql/pymysql.py +137 -0
  48. sqlalchemy/dialects/mysql/pyodbc.py +138 -0
  49. sqlalchemy/dialects/mysql/reflection.py +677 -0
  50. sqlalchemy/dialects/mysql/reserved_words.py +571 -0
  51. sqlalchemy/dialects/mysql/types.py +774 -0
  52. sqlalchemy/dialects/oracle/__init__.py +67 -0
  53. sqlalchemy/dialects/oracle/base.py +3271 -0
  54. sqlalchemy/dialects/oracle/cx_oracle.py +1483 -0
  55. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  56. sqlalchemy/dialects/oracle/oracledb.py +431 -0
  57. sqlalchemy/dialects/oracle/provision.py +220 -0
  58. sqlalchemy/dialects/oracle/types.py +287 -0
  59. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  60. sqlalchemy/dialects/postgresql/_psycopg_common.py +187 -0
  61. sqlalchemy/dialects/postgresql/array.py +425 -0
  62. sqlalchemy/dialects/postgresql/asyncpg.py +1274 -0
  63. sqlalchemy/dialects/postgresql/base.py +5008 -0
  64. sqlalchemy/dialects/postgresql/dml.py +310 -0
  65. sqlalchemy/dialects/postgresql/ext.py +496 -0
  66. sqlalchemy/dialects/postgresql/hstore.py +397 -0
  67. sqlalchemy/dialects/postgresql/json.py +333 -0
  68. sqlalchemy/dialects/postgresql/named_types.py +509 -0
  69. sqlalchemy/dialects/postgresql/operators.py +129 -0
  70. sqlalchemy/dialects/postgresql/pg8000.py +662 -0
  71. sqlalchemy/dialects/postgresql/pg_catalog.py +300 -0
  72. sqlalchemy/dialects/postgresql/provision.py +175 -0
  73. sqlalchemy/dialects/postgresql/psycopg.py +772 -0
  74. sqlalchemy/dialects/postgresql/psycopg2.py +886 -0
  75. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  76. sqlalchemy/dialects/postgresql/ranges.py +1029 -0
  77. sqlalchemy/dialects/postgresql/types.py +303 -0
  78. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  79. sqlalchemy/dialects/sqlite/aiosqlite.py +396 -0
  80. sqlalchemy/dialects/sqlite/base.py +2805 -0
  81. sqlalchemy/dialects/sqlite/dml.py +240 -0
  82. sqlalchemy/dialects/sqlite/json.py +92 -0
  83. sqlalchemy/dialects/sqlite/provision.py +198 -0
  84. sqlalchemy/dialects/sqlite/pysqlcipher.py +155 -0
  85. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  86. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  87. sqlalchemy/engine/__init__.py +62 -0
  88. sqlalchemy/engine/_py_processors.py +136 -0
  89. sqlalchemy/engine/_py_row.py +128 -0
  90. sqlalchemy/engine/_py_util.py +74 -0
  91. sqlalchemy/engine/base.py +3375 -0
  92. sqlalchemy/engine/characteristics.py +155 -0
  93. sqlalchemy/engine/create.py +875 -0
  94. sqlalchemy/engine/cursor.py +2181 -0
  95. sqlalchemy/engine/default.py +2365 -0
  96. sqlalchemy/engine/events.py +951 -0
  97. sqlalchemy/engine/interfaces.py +3403 -0
  98. sqlalchemy/engine/mock.py +131 -0
  99. sqlalchemy/engine/processors.py +61 -0
  100. sqlalchemy/engine/reflection.py +2098 -0
  101. sqlalchemy/engine/result.py +2382 -0
  102. sqlalchemy/engine/row.py +401 -0
  103. sqlalchemy/engine/strategies.py +19 -0
  104. sqlalchemy/engine/url.py +910 -0
  105. sqlalchemy/engine/util.py +167 -0
  106. sqlalchemy/event/__init__.py +25 -0
  107. sqlalchemy/event/api.py +225 -0
  108. sqlalchemy/event/attr.py +655 -0
  109. sqlalchemy/event/base.py +470 -0
  110. sqlalchemy/event/legacy.py +246 -0
  111. sqlalchemy/event/registry.py +386 -0
  112. sqlalchemy/events.py +17 -0
  113. sqlalchemy/exc.py +830 -0
  114. sqlalchemy/ext/__init__.py +11 -0
  115. sqlalchemy/ext/associationproxy.py +2013 -0
  116. sqlalchemy/ext/asyncio/__init__.py +25 -0
  117. sqlalchemy/ext/asyncio/base.py +279 -0
  118. sqlalchemy/ext/asyncio/engine.py +1466 -0
  119. sqlalchemy/ext/asyncio/exc.py +21 -0
  120. sqlalchemy/ext/asyncio/result.py +961 -0
  121. sqlalchemy/ext/asyncio/scoping.py +1614 -0
  122. sqlalchemy/ext/asyncio/session.py +1936 -0
  123. sqlalchemy/ext/automap.py +1691 -0
  124. sqlalchemy/ext/baked.py +574 -0
  125. sqlalchemy/ext/compiler.py +570 -0
  126. sqlalchemy/ext/declarative/__init__.py +65 -0
  127. sqlalchemy/ext/declarative/extensions.py +548 -0
  128. sqlalchemy/ext/horizontal_shard.py +481 -0
  129. sqlalchemy/ext/hybrid.py +1514 -0
  130. sqlalchemy/ext/indexable.py +341 -0
  131. sqlalchemy/ext/instrumentation.py +450 -0
  132. sqlalchemy/ext/mutable.py +1073 -0
  133. sqlalchemy/ext/mypy/__init__.py +6 -0
  134. sqlalchemy/ext/mypy/apply.py +320 -0
  135. sqlalchemy/ext/mypy/decl_class.py +515 -0
  136. sqlalchemy/ext/mypy/infer.py +590 -0
  137. sqlalchemy/ext/mypy/names.py +335 -0
  138. sqlalchemy/ext/mypy/plugin.py +303 -0
  139. sqlalchemy/ext/mypy/util.py +357 -0
  140. sqlalchemy/ext/orderinglist.py +416 -0
  141. sqlalchemy/ext/serializer.py +181 -0
  142. sqlalchemy/future/__init__.py +16 -0
  143. sqlalchemy/future/engine.py +15 -0
  144. sqlalchemy/inspection.py +174 -0
  145. sqlalchemy/log.py +288 -0
  146. sqlalchemy/orm/__init__.py +170 -0
  147. sqlalchemy/orm/_orm_constructors.py +2571 -0
  148. sqlalchemy/orm/_typing.py +179 -0
  149. sqlalchemy/orm/attributes.py +2835 -0
  150. sqlalchemy/orm/base.py +973 -0
  151. sqlalchemy/orm/bulk_persistence.py +2123 -0
  152. sqlalchemy/orm/clsregistry.py +571 -0
  153. sqlalchemy/orm/collections.py +1620 -0
  154. sqlalchemy/orm/context.py +3268 -0
  155. sqlalchemy/orm/decl_api.py +1883 -0
  156. sqlalchemy/orm/decl_base.py +2190 -0
  157. sqlalchemy/orm/dependency.py +1304 -0
  158. sqlalchemy/orm/descriptor_props.py +1076 -0
  159. sqlalchemy/orm/dynamic.py +300 -0
  160. sqlalchemy/orm/evaluator.py +379 -0
  161. sqlalchemy/orm/events.py +3261 -0
  162. sqlalchemy/orm/exc.py +228 -0
  163. sqlalchemy/orm/identity.py +302 -0
  164. sqlalchemy/orm/instrumentation.py +754 -0
  165. sqlalchemy/orm/interfaces.py +1474 -0
  166. sqlalchemy/orm/loading.py +1682 -0
  167. sqlalchemy/orm/mapped_collection.py +557 -0
  168. sqlalchemy/orm/mapper.py +4432 -0
  169. sqlalchemy/orm/path_registry.py +811 -0
  170. sqlalchemy/orm/persistence.py +1782 -0
  171. sqlalchemy/orm/properties.py +886 -0
  172. sqlalchemy/orm/query.py +3396 -0
  173. sqlalchemy/orm/relationships.py +3500 -0
  174. sqlalchemy/orm/scoping.py +2165 -0
  175. sqlalchemy/orm/session.py +5301 -0
  176. sqlalchemy/orm/state.py +1143 -0
  177. sqlalchemy/orm/state_changes.py +198 -0
  178. sqlalchemy/orm/strategies.py +3473 -0
  179. sqlalchemy/orm/strategy_options.py +2569 -0
  180. sqlalchemy/orm/sync.py +164 -0
  181. sqlalchemy/orm/unitofwork.py +796 -0
  182. sqlalchemy/orm/util.py +2424 -0
  183. sqlalchemy/orm/writeonly.py +678 -0
  184. sqlalchemy/pool/__init__.py +44 -0
  185. sqlalchemy/pool/base.py +1515 -0
  186. sqlalchemy/pool/events.py +370 -0
  187. sqlalchemy/pool/impl.py +581 -0
  188. sqlalchemy/py.typed +0 -0
  189. sqlalchemy/schema.py +70 -0
  190. sqlalchemy/sql/__init__.py +145 -0
  191. sqlalchemy/sql/_dml_constructors.py +140 -0
  192. sqlalchemy/sql/_elements_constructors.py +1850 -0
  193. sqlalchemy/sql/_orm_types.py +20 -0
  194. sqlalchemy/sql/_py_util.py +75 -0
  195. sqlalchemy/sql/_selectable_constructors.py +635 -0
  196. sqlalchemy/sql/_typing.py +460 -0
  197. sqlalchemy/sql/annotation.py +585 -0
  198. sqlalchemy/sql/base.py +2185 -0
  199. sqlalchemy/sql/cache_key.py +1057 -0
  200. sqlalchemy/sql/coercions.py +1405 -0
  201. sqlalchemy/sql/compiler.py +7818 -0
  202. sqlalchemy/sql/crud.py +1669 -0
  203. sqlalchemy/sql/ddl.py +1378 -0
  204. sqlalchemy/sql/default_comparator.py +552 -0
  205. sqlalchemy/sql/dml.py +1817 -0
  206. sqlalchemy/sql/elements.py +5499 -0
  207. sqlalchemy/sql/events.py +455 -0
  208. sqlalchemy/sql/expression.py +162 -0
  209. sqlalchemy/sql/functions.py +2055 -0
  210. sqlalchemy/sql/lambdas.py +1449 -0
  211. sqlalchemy/sql/naming.py +212 -0
  212. sqlalchemy/sql/operators.py +2579 -0
  213. sqlalchemy/sql/roles.py +323 -0
  214. sqlalchemy/sql/schema.py +6158 -0
  215. sqlalchemy/sql/selectable.py +7004 -0
  216. sqlalchemy/sql/sqltypes.py +3827 -0
  217. sqlalchemy/sql/traversals.py +1024 -0
  218. sqlalchemy/sql/type_api.py +2339 -0
  219. sqlalchemy/sql/util.py +1486 -0
  220. sqlalchemy/sql/visitors.py +1165 -0
  221. sqlalchemy/testing/__init__.py +96 -0
  222. sqlalchemy/testing/assertions.py +989 -0
  223. sqlalchemy/testing/assertsql.py +516 -0
  224. sqlalchemy/testing/asyncio.py +135 -0
  225. sqlalchemy/testing/config.py +427 -0
  226. sqlalchemy/testing/engines.py +472 -0
  227. sqlalchemy/testing/entities.py +117 -0
  228. sqlalchemy/testing/exclusions.py +435 -0
  229. sqlalchemy/testing/fixtures/__init__.py +28 -0
  230. sqlalchemy/testing/fixtures/base.py +366 -0
  231. sqlalchemy/testing/fixtures/mypy.py +312 -0
  232. sqlalchemy/testing/fixtures/orm.py +227 -0
  233. sqlalchemy/testing/fixtures/sql.py +503 -0
  234. sqlalchemy/testing/pickleable.py +155 -0
  235. sqlalchemy/testing/plugin/__init__.py +6 -0
  236. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  237. sqlalchemy/testing/plugin/plugin_base.py +779 -0
  238. sqlalchemy/testing/plugin/pytestplugin.py +868 -0
  239. sqlalchemy/testing/profiling.py +324 -0
  240. sqlalchemy/testing/provision.py +496 -0
  241. sqlalchemy/testing/requirements.py +1818 -0
  242. sqlalchemy/testing/schema.py +224 -0
  243. sqlalchemy/testing/suite/__init__.py +19 -0
  244. sqlalchemy/testing/suite/test_cte.py +211 -0
  245. sqlalchemy/testing/suite/test_ddl.py +389 -0
  246. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  247. sqlalchemy/testing/suite/test_dialect.py +740 -0
  248. sqlalchemy/testing/suite/test_insert.py +630 -0
  249. sqlalchemy/testing/suite/test_reflection.py +3225 -0
  250. sqlalchemy/testing/suite/test_results.py +502 -0
  251. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  252. sqlalchemy/testing/suite/test_select.py +1999 -0
  253. sqlalchemy/testing/suite/test_sequence.py +317 -0
  254. sqlalchemy/testing/suite/test_types.py +2141 -0
  255. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  256. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  257. sqlalchemy/testing/util.py +537 -0
  258. sqlalchemy/testing/warnings.py +52 -0
  259. sqlalchemy/types.py +76 -0
  260. sqlalchemy/util/__init__.py +160 -0
  261. sqlalchemy/util/_collections.py +715 -0
  262. sqlalchemy/util/_concurrency_py3k.py +288 -0
  263. sqlalchemy/util/_has_cy.py +40 -0
  264. sqlalchemy/util/_py_collections.py +541 -0
  265. sqlalchemy/util/compat.py +301 -0
  266. sqlalchemy/util/concurrency.py +108 -0
  267. sqlalchemy/util/deprecations.py +401 -0
  268. sqlalchemy/util/langhelpers.py +2218 -0
  269. sqlalchemy/util/preloaded.py +150 -0
  270. sqlalchemy/util/queue.py +322 -0
  271. sqlalchemy/util/tool_support.py +201 -0
  272. sqlalchemy/util/topological.py +120 -0
  273. sqlalchemy/util/typing.py +629 -0
@@ -0,0 +1,254 @@
1
+ # dialects/mssql/information_schema.py
2
+ # Copyright (C) 2005-2024 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
+ mssql_temp_table_columns = Table(
92
+ "COLUMNS",
93
+ ischema,
94
+ Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
95
+ Column("TABLE_NAME", CoerceUnicode, key="table_name"),
96
+ Column("COLUMN_NAME", CoerceUnicode, key="column_name"),
97
+ Column("IS_NULLABLE", Integer, key="is_nullable"),
98
+ Column("DATA_TYPE", String, key="data_type"),
99
+ Column("ORDINAL_POSITION", Integer, key="ordinal_position"),
100
+ Column(
101
+ "CHARACTER_MAXIMUM_LENGTH", Integer, key="character_maximum_length"
102
+ ),
103
+ Column("NUMERIC_PRECISION", Integer, key="numeric_precision"),
104
+ Column("NUMERIC_SCALE", Integer, key="numeric_scale"),
105
+ Column("COLUMN_DEFAULT", Integer, key="column_default"),
106
+ Column("COLLATION_NAME", String, key="collation_name"),
107
+ schema="tempdb.INFORMATION_SCHEMA",
108
+ )
109
+
110
+ constraints = Table(
111
+ "TABLE_CONSTRAINTS",
112
+ ischema,
113
+ Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
114
+ Column("TABLE_NAME", CoerceUnicode, key="table_name"),
115
+ Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"),
116
+ Column("CONSTRAINT_TYPE", CoerceUnicode, key="constraint_type"),
117
+ schema="INFORMATION_SCHEMA",
118
+ )
119
+
120
+ column_constraints = Table(
121
+ "CONSTRAINT_COLUMN_USAGE",
122
+ ischema,
123
+ Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
124
+ Column("TABLE_NAME", CoerceUnicode, key="table_name"),
125
+ Column("COLUMN_NAME", CoerceUnicode, key="column_name"),
126
+ Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"),
127
+ schema="INFORMATION_SCHEMA",
128
+ )
129
+
130
+ key_constraints = Table(
131
+ "KEY_COLUMN_USAGE",
132
+ ischema,
133
+ Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
134
+ Column("TABLE_NAME", CoerceUnicode, key="table_name"),
135
+ Column("COLUMN_NAME", CoerceUnicode, key="column_name"),
136
+ Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"),
137
+ Column("CONSTRAINT_SCHEMA", CoerceUnicode, key="constraint_schema"),
138
+ Column("ORDINAL_POSITION", Integer, key="ordinal_position"),
139
+ schema="INFORMATION_SCHEMA",
140
+ )
141
+
142
+ ref_constraints = Table(
143
+ "REFERENTIAL_CONSTRAINTS",
144
+ ischema,
145
+ Column("CONSTRAINT_CATALOG", CoerceUnicode, key="constraint_catalog"),
146
+ Column("CONSTRAINT_SCHEMA", CoerceUnicode, key="constraint_schema"),
147
+ Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"),
148
+ # TODO: is CATLOG misspelled ?
149
+ Column(
150
+ "UNIQUE_CONSTRAINT_CATLOG",
151
+ CoerceUnicode,
152
+ key="unique_constraint_catalog",
153
+ ),
154
+ Column(
155
+ "UNIQUE_CONSTRAINT_SCHEMA",
156
+ CoerceUnicode,
157
+ key="unique_constraint_schema",
158
+ ),
159
+ Column(
160
+ "UNIQUE_CONSTRAINT_NAME", CoerceUnicode, key="unique_constraint_name"
161
+ ),
162
+ Column("MATCH_OPTION", String, key="match_option"),
163
+ Column("UPDATE_RULE", String, key="update_rule"),
164
+ Column("DELETE_RULE", String, key="delete_rule"),
165
+ schema="INFORMATION_SCHEMA",
166
+ )
167
+
168
+ views = Table(
169
+ "VIEWS",
170
+ ischema,
171
+ Column("TABLE_CATALOG", CoerceUnicode, key="table_catalog"),
172
+ Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"),
173
+ Column("TABLE_NAME", CoerceUnicode, key="table_name"),
174
+ Column("VIEW_DEFINITION", CoerceUnicode, key="view_definition"),
175
+ Column("CHECK_OPTION", String, key="check_option"),
176
+ Column("IS_UPDATABLE", String, key="is_updatable"),
177
+ schema="INFORMATION_SCHEMA",
178
+ )
179
+
180
+ computed_columns = Table(
181
+ "computed_columns",
182
+ ischema,
183
+ Column("object_id", Integer),
184
+ Column("name", CoerceUnicode),
185
+ Column("is_computed", Boolean),
186
+ Column("is_persisted", Boolean),
187
+ Column("definition", CoerceUnicode),
188
+ schema="sys",
189
+ )
190
+
191
+ sequences = Table(
192
+ "SEQUENCES",
193
+ ischema,
194
+ Column("SEQUENCE_CATALOG", CoerceUnicode, key="sequence_catalog"),
195
+ Column("SEQUENCE_SCHEMA", CoerceUnicode, key="sequence_schema"),
196
+ Column("SEQUENCE_NAME", CoerceUnicode, key="sequence_name"),
197
+ schema="INFORMATION_SCHEMA",
198
+ )
199
+
200
+
201
+ class NumericSqlVariant(TypeDecorator):
202
+ r"""This type casts sql_variant columns in the identity_columns view
203
+ to numeric. This is required because:
204
+
205
+ * pyodbc does not support sql_variant
206
+ * pymssql under python 2 return the byte representation of the number,
207
+ int 1 is returned as "\x01\x00\x00\x00". On python 3 it returns the
208
+ correct value as string.
209
+ """
210
+
211
+ impl = Unicode
212
+ cache_ok = True
213
+
214
+ def column_expression(self, colexpr):
215
+ return cast(colexpr, Numeric(38, 0))
216
+
217
+
218
+ identity_columns = Table(
219
+ "identity_columns",
220
+ ischema,
221
+ Column("object_id", Integer),
222
+ Column("name", CoerceUnicode),
223
+ Column("is_identity", Boolean),
224
+ Column("seed_value", NumericSqlVariant),
225
+ Column("increment_value", NumericSqlVariant),
226
+ Column("last_value", NumericSqlVariant),
227
+ Column("is_not_for_replication", Boolean),
228
+ schema="sys",
229
+ )
230
+
231
+
232
+ class NVarcharSqlVariant(TypeDecorator):
233
+ """This type casts sql_variant columns in the extended_properties view
234
+ to nvarchar. This is required because pyodbc does not support sql_variant
235
+ """
236
+
237
+ impl = Unicode
238
+ cache_ok = True
239
+
240
+ def column_expression(self, colexpr):
241
+ return cast(colexpr, NVARCHAR)
242
+
243
+
244
+ extended_properties = Table(
245
+ "extended_properties",
246
+ ischema,
247
+ Column("class", Integer), # TINYINT
248
+ Column("class_desc", CoerceUnicode),
249
+ Column("major_id", Integer),
250
+ Column("minor_id", Integer),
251
+ Column("name", CoerceUnicode),
252
+ Column("value", NVarcharSqlVariant),
253
+ schema="sys",
254
+ )
@@ -0,0 +1,133 @@
1
+ # dialects/mssql/json.py
2
+ # Copyright (C) 2005-2024 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 types as sqltypes
10
+
11
+ # technically, all the dialect-specific datatypes that don't have any special
12
+ # behaviors would be private with names like _MSJson. However, we haven't been
13
+ # doing this for mysql.JSON or sqlite.JSON which both have JSON / JSONIndexType
14
+ # / JSONPathType in their json.py files, so keep consistent with that
15
+ # sub-convention for now. A future change can update them all to be
16
+ # package-private at once.
17
+
18
+
19
+ class JSON(sqltypes.JSON):
20
+ """MSSQL JSON type.
21
+
22
+ MSSQL supports JSON-formatted data as of SQL Server 2016.
23
+
24
+ The :class:`_mssql.JSON` datatype at the DDL level will represent the
25
+ datatype as ``NVARCHAR(max)``, but provides for JSON-level comparison
26
+ functions as well as Python coercion behavior.
27
+
28
+ :class:`_mssql.JSON` is used automatically whenever the base
29
+ :class:`_types.JSON` datatype is used against a SQL Server backend.
30
+
31
+ .. seealso::
32
+
33
+ :class:`_types.JSON` - main documentation for the generic
34
+ cross-platform JSON datatype.
35
+
36
+ The :class:`_mssql.JSON` type supports persistence of JSON values
37
+ as well as the core index operations provided by :class:`_types.JSON`
38
+ datatype, by adapting the operations to render the ``JSON_VALUE``
39
+ or ``JSON_QUERY`` functions at the database level.
40
+
41
+ The SQL Server :class:`_mssql.JSON` type necessarily makes use of the
42
+ ``JSON_QUERY`` and ``JSON_VALUE`` functions when querying for elements
43
+ of a JSON object. These two functions have a major restriction in that
44
+ they are **mutually exclusive** based on the type of object to be returned.
45
+ The ``JSON_QUERY`` function **only** returns a JSON dictionary or list,
46
+ but not an individual string, numeric, or boolean element; the
47
+ ``JSON_VALUE`` function **only** returns an individual string, numeric,
48
+ or boolean element. **both functions either return NULL or raise
49
+ an error if they are not used against the correct expected value**.
50
+
51
+ To handle this awkward requirement, indexed access rules are as follows:
52
+
53
+ 1. When extracting a sub element from a JSON that is itself a JSON
54
+ dictionary or list, the :meth:`_types.JSON.Comparator.as_json` accessor
55
+ should be used::
56
+
57
+ stmt = select(
58
+ data_table.c.data["some key"].as_json()
59
+ ).where(
60
+ data_table.c.data["some key"].as_json() == {"sub": "structure"}
61
+ )
62
+
63
+ 2. When extracting a sub element from a JSON that is a plain boolean,
64
+ string, integer, or float, use the appropriate method among
65
+ :meth:`_types.JSON.Comparator.as_boolean`,
66
+ :meth:`_types.JSON.Comparator.as_string`,
67
+ :meth:`_types.JSON.Comparator.as_integer`,
68
+ :meth:`_types.JSON.Comparator.as_float`::
69
+
70
+ stmt = select(
71
+ data_table.c.data["some key"].as_string()
72
+ ).where(
73
+ data_table.c.data["some key"].as_string() == "some string"
74
+ )
75
+
76
+ .. versionadded:: 1.4
77
+
78
+
79
+ """
80
+
81
+ # note there was a result processor here that was looking for "number",
82
+ # but none of the tests seem to exercise it.
83
+
84
+
85
+ # Note: these objects currently match exactly those of MySQL, however since
86
+ # these are not generalizable to all JSON implementations, remain separately
87
+ # implemented for each dialect.
88
+ class _FormatTypeMixin:
89
+ def _format_value(self, value):
90
+ raise NotImplementedError()
91
+
92
+ def bind_processor(self, dialect):
93
+ super_proc = self.string_bind_processor(dialect)
94
+
95
+ def process(value):
96
+ value = self._format_value(value)
97
+ if super_proc:
98
+ value = super_proc(value)
99
+ return value
100
+
101
+ return process
102
+
103
+ def literal_processor(self, dialect):
104
+ super_proc = self.string_literal_processor(dialect)
105
+
106
+ def process(value):
107
+ value = self._format_value(value)
108
+ if super_proc:
109
+ value = super_proc(value)
110
+ return value
111
+
112
+ return process
113
+
114
+
115
+ class JSONIndexType(_FormatTypeMixin, sqltypes.JSON.JSONIndexType):
116
+ def _format_value(self, value):
117
+ if isinstance(value, int):
118
+ value = "$[%s]" % value
119
+ else:
120
+ value = '$."%s"' % value
121
+ return value
122
+
123
+
124
+ class JSONPathType(_FormatTypeMixin, sqltypes.JSON.JSONPathType):
125
+ def _format_value(self, value):
126
+ return "$%s" % (
127
+ "".join(
128
+ [
129
+ "[%s]" % elem if isinstance(elem, int) else '."%s"' % elem
130
+ for elem in value
131
+ ]
132
+ )
133
+ )
@@ -0,0 +1,162 @@
1
+ # dialects/mssql/provision.py
2
+ # Copyright (C) 2005-2024 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 drop_all_schema_objects_pre_tables
20
+ from ...testing.provision import drop_db
21
+ from ...testing.provision import generate_driver_url
22
+ from ...testing.provision import get_temp_table_name
23
+ from ...testing.provision import log
24
+ from ...testing.provision import normalize_sequence
25
+ from ...testing.provision import post_configure_engine
26
+ from ...testing.provision import run_reap_dbs
27
+ from ...testing.provision import temp_table_keyword_args
28
+
29
+
30
+ @post_configure_engine.for_db("mssql")
31
+ def post_configure_engine(url, engine, follower_ident):
32
+ if engine.driver == "pyodbc":
33
+ engine.dialect.dbapi.pooling = False
34
+
35
+
36
+ @generate_driver_url.for_db("mssql")
37
+ def generate_driver_url(url, driver, query_str):
38
+ backend = url.get_backend_name()
39
+
40
+ new_url = url.set(drivername="%s+%s" % (backend, driver))
41
+
42
+ if driver not in ("pyodbc", "aioodbc"):
43
+ new_url = new_url.set(query="")
44
+
45
+ if driver == "aioodbc":
46
+ new_url = new_url.update_query_dict({"MARS_Connection": "Yes"})
47
+
48
+ if query_str:
49
+ new_url = new_url.update_query_string(query_str)
50
+
51
+ try:
52
+ new_url.get_dialect()
53
+ except exc.NoSuchModuleError:
54
+ return None
55
+ else:
56
+ return new_url
57
+
58
+
59
+ @create_db.for_db("mssql")
60
+ def _mssql_create_db(cfg, eng, ident):
61
+ with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
62
+ conn.exec_driver_sql("create database %s" % ident)
63
+ conn.exec_driver_sql(
64
+ "ALTER DATABASE %s SET ALLOW_SNAPSHOT_ISOLATION ON" % ident
65
+ )
66
+ conn.exec_driver_sql(
67
+ "ALTER DATABASE %s SET READ_COMMITTED_SNAPSHOT ON" % ident
68
+ )
69
+ conn.exec_driver_sql("use %s" % ident)
70
+ conn.exec_driver_sql("create schema test_schema")
71
+ conn.exec_driver_sql("create schema test_schema_2")
72
+
73
+
74
+ @drop_db.for_db("mssql")
75
+ def _mssql_drop_db(cfg, eng, ident):
76
+ with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
77
+ _mssql_drop_ignore(conn, ident)
78
+
79
+
80
+ def _mssql_drop_ignore(conn, ident):
81
+ try:
82
+ # typically when this happens, we can't KILL the session anyway,
83
+ # so let the cleanup process drop the DBs
84
+ # for row in conn.exec_driver_sql(
85
+ # "select session_id from sys.dm_exec_sessions "
86
+ # "where database_id=db_id('%s')" % ident):
87
+ # log.info("killing SQL server session %s", row['session_id'])
88
+ # conn.exec_driver_sql("kill %s" % row['session_id'])
89
+ conn.exec_driver_sql("drop database %s" % ident)
90
+ log.info("Reaped db: %s", ident)
91
+ return True
92
+ except exc.DatabaseError as err:
93
+ log.warning("couldn't drop db: %s", err)
94
+ return False
95
+
96
+
97
+ @run_reap_dbs.for_db("mssql")
98
+ def _reap_mssql_dbs(url, idents):
99
+ log.info("db reaper connecting to %r", url)
100
+ eng = create_engine(url)
101
+ with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
102
+ log.info("identifiers in file: %s", ", ".join(idents))
103
+
104
+ to_reap = conn.exec_driver_sql(
105
+ "select d.name from sys.databases as d where name "
106
+ "like 'TEST_%' and not exists (select session_id "
107
+ "from sys.dm_exec_sessions "
108
+ "where database_id=d.database_id)"
109
+ )
110
+ all_names = {dbname.lower() for (dbname,) in to_reap}
111
+ to_drop = set()
112
+ for name in all_names:
113
+ if name in idents:
114
+ to_drop.add(name)
115
+
116
+ dropped = total = 0
117
+ for total, dbname in enumerate(to_drop, 1):
118
+ if _mssql_drop_ignore(conn, dbname):
119
+ dropped += 1
120
+ log.info(
121
+ "Dropped %d out of %d stale databases detected", dropped, total
122
+ )
123
+
124
+
125
+ @temp_table_keyword_args.for_db("mssql")
126
+ def _mssql_temp_table_keyword_args(cfg, eng):
127
+ return {}
128
+
129
+
130
+ @get_temp_table_name.for_db("mssql")
131
+ def _mssql_get_temp_table_name(cfg, eng, base_name):
132
+ return "##" + base_name
133
+
134
+
135
+ @drop_all_schema_objects_pre_tables.for_db("mssql")
136
+ def drop_all_schema_objects_pre_tables(cfg, eng):
137
+ with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
138
+ inspector = inspect(conn)
139
+ for schema in (None, "dbo", cfg.test_schema, cfg.test_schema_2):
140
+ for tname in inspector.get_table_names(schema=schema):
141
+ tb = Table(
142
+ tname,
143
+ MetaData(),
144
+ Column("x", Integer),
145
+ Column("y", Integer),
146
+ schema=schema,
147
+ )
148
+ for fk in inspect(conn).get_foreign_keys(tname, schema=schema):
149
+ conn.execute(
150
+ DropConstraint(
151
+ ForeignKeyConstraint(
152
+ [tb.c.x], [tb.c.y], name=fk["name"]
153
+ )
154
+ )
155
+ )
156
+
157
+
158
+ @normalize_sequence.for_db("mssql")
159
+ def normalize_sequence(cfg, sequence):
160
+ if sequence.start is None:
161
+ sequence.start = 1
162
+ return sequence
@@ -0,0 +1,126 @@
1
+ # dialects/mssql/pymssql.py
2
+ # Copyright (C) 2005-2024 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