SQLAlchemy 2.0.47__cp313-cp313t-win_amd64.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 (274) hide show
  1. sqlalchemy/__init__.py +283 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +184 -0
  4. sqlalchemy/connectors/asyncio.py +429 -0
  5. sqlalchemy/connectors/pyodbc.py +250 -0
  6. sqlalchemy/cyextension/__init__.py +6 -0
  7. sqlalchemy/cyextension/collections.cp313t-win_amd64.pyd +0 -0
  8. sqlalchemy/cyextension/collections.pyx +409 -0
  9. sqlalchemy/cyextension/immutabledict.cp313t-win_amd64.pyd +0 -0
  10. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  11. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  12. sqlalchemy/cyextension/processors.cp313t-win_amd64.pyd +0 -0
  13. sqlalchemy/cyextension/processors.pyx +68 -0
  14. sqlalchemy/cyextension/resultproxy.cp313t-win_amd64.pyd +0 -0
  15. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  16. sqlalchemy/cyextension/util.cp313t-win_amd64.pyd +0 -0
  17. sqlalchemy/cyextension/util.pyx +90 -0
  18. sqlalchemy/dialects/__init__.py +62 -0
  19. sqlalchemy/dialects/_typing.py +30 -0
  20. sqlalchemy/dialects/mssql/__init__.py +88 -0
  21. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  22. sqlalchemy/dialects/mssql/base.py +4093 -0
  23. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  24. sqlalchemy/dialects/mssql/json.py +129 -0
  25. sqlalchemy/dialects/mssql/provision.py +185 -0
  26. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  27. sqlalchemy/dialects/mssql/pyodbc.py +760 -0
  28. sqlalchemy/dialects/mysql/__init__.py +104 -0
  29. sqlalchemy/dialects/mysql/aiomysql.py +250 -0
  30. sqlalchemy/dialects/mysql/asyncmy.py +231 -0
  31. sqlalchemy/dialects/mysql/base.py +3949 -0
  32. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  33. sqlalchemy/dialects/mysql/dml.py +225 -0
  34. sqlalchemy/dialects/mysql/enumerated.py +282 -0
  35. sqlalchemy/dialects/mysql/expression.py +146 -0
  36. sqlalchemy/dialects/mysql/json.py +91 -0
  37. sqlalchemy/dialects/mysql/mariadb.py +72 -0
  38. sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
  39. sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
  40. sqlalchemy/dialects/mysql/mysqldb.py +314 -0
  41. sqlalchemy/dialects/mysql/provision.py +153 -0
  42. sqlalchemy/dialects/mysql/pymysql.py +158 -0
  43. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  44. sqlalchemy/dialects/mysql/reflection.py +727 -0
  45. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  46. sqlalchemy/dialects/mysql/types.py +835 -0
  47. sqlalchemy/dialects/oracle/__init__.py +81 -0
  48. sqlalchemy/dialects/oracle/base.py +3802 -0
  49. sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
  50. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  51. sqlalchemy/dialects/oracle/oracledb.py +941 -0
  52. sqlalchemy/dialects/oracle/provision.py +297 -0
  53. sqlalchemy/dialects/oracle/types.py +316 -0
  54. sqlalchemy/dialects/oracle/vector.py +365 -0
  55. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  56. sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
  57. sqlalchemy/dialects/postgresql/array.py +519 -0
  58. sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
  59. sqlalchemy/dialects/postgresql/base.py +5378 -0
  60. sqlalchemy/dialects/postgresql/dml.py +339 -0
  61. sqlalchemy/dialects/postgresql/ext.py +540 -0
  62. sqlalchemy/dialects/postgresql/hstore.py +406 -0
  63. sqlalchemy/dialects/postgresql/json.py +404 -0
  64. sqlalchemy/dialects/postgresql/named_types.py +524 -0
  65. sqlalchemy/dialects/postgresql/operators.py +129 -0
  66. sqlalchemy/dialects/postgresql/pg8000.py +669 -0
  67. sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
  68. sqlalchemy/dialects/postgresql/provision.py +183 -0
  69. sqlalchemy/dialects/postgresql/psycopg.py +862 -0
  70. sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
  71. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  72. sqlalchemy/dialects/postgresql/ranges.py +1031 -0
  73. sqlalchemy/dialects/postgresql/types.py +313 -0
  74. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  75. sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
  76. sqlalchemy/dialects/sqlite/base.py +3056 -0
  77. sqlalchemy/dialects/sqlite/dml.py +263 -0
  78. sqlalchemy/dialects/sqlite/json.py +92 -0
  79. sqlalchemy/dialects/sqlite/provision.py +229 -0
  80. sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
  81. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  82. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  83. sqlalchemy/engine/__init__.py +62 -0
  84. sqlalchemy/engine/_py_processors.py +136 -0
  85. sqlalchemy/engine/_py_row.py +128 -0
  86. sqlalchemy/engine/_py_util.py +74 -0
  87. sqlalchemy/engine/base.py +3390 -0
  88. sqlalchemy/engine/characteristics.py +155 -0
  89. sqlalchemy/engine/create.py +893 -0
  90. sqlalchemy/engine/cursor.py +2298 -0
  91. sqlalchemy/engine/default.py +2394 -0
  92. sqlalchemy/engine/events.py +965 -0
  93. sqlalchemy/engine/interfaces.py +3471 -0
  94. sqlalchemy/engine/mock.py +134 -0
  95. sqlalchemy/engine/processors.py +61 -0
  96. sqlalchemy/engine/reflection.py +2102 -0
  97. sqlalchemy/engine/result.py +2399 -0
  98. sqlalchemy/engine/row.py +400 -0
  99. sqlalchemy/engine/strategies.py +16 -0
  100. sqlalchemy/engine/url.py +924 -0
  101. sqlalchemy/engine/util.py +167 -0
  102. sqlalchemy/event/__init__.py +26 -0
  103. sqlalchemy/event/api.py +220 -0
  104. sqlalchemy/event/attr.py +676 -0
  105. sqlalchemy/event/base.py +472 -0
  106. sqlalchemy/event/legacy.py +258 -0
  107. sqlalchemy/event/registry.py +390 -0
  108. sqlalchemy/events.py +17 -0
  109. sqlalchemy/exc.py +832 -0
  110. sqlalchemy/ext/__init__.py +11 -0
  111. sqlalchemy/ext/associationproxy.py +2027 -0
  112. sqlalchemy/ext/asyncio/__init__.py +25 -0
  113. sqlalchemy/ext/asyncio/base.py +281 -0
  114. sqlalchemy/ext/asyncio/engine.py +1471 -0
  115. sqlalchemy/ext/asyncio/exc.py +21 -0
  116. sqlalchemy/ext/asyncio/result.py +965 -0
  117. sqlalchemy/ext/asyncio/scoping.py +1599 -0
  118. sqlalchemy/ext/asyncio/session.py +1947 -0
  119. sqlalchemy/ext/automap.py +1701 -0
  120. sqlalchemy/ext/baked.py +570 -0
  121. sqlalchemy/ext/compiler.py +600 -0
  122. sqlalchemy/ext/declarative/__init__.py +65 -0
  123. sqlalchemy/ext/declarative/extensions.py +564 -0
  124. sqlalchemy/ext/horizontal_shard.py +478 -0
  125. sqlalchemy/ext/hybrid.py +1535 -0
  126. sqlalchemy/ext/indexable.py +364 -0
  127. sqlalchemy/ext/instrumentation.py +450 -0
  128. sqlalchemy/ext/mutable.py +1085 -0
  129. sqlalchemy/ext/mypy/__init__.py +6 -0
  130. sqlalchemy/ext/mypy/apply.py +324 -0
  131. sqlalchemy/ext/mypy/decl_class.py +515 -0
  132. sqlalchemy/ext/mypy/infer.py +590 -0
  133. sqlalchemy/ext/mypy/names.py +335 -0
  134. sqlalchemy/ext/mypy/plugin.py +303 -0
  135. sqlalchemy/ext/mypy/util.py +357 -0
  136. sqlalchemy/ext/orderinglist.py +439 -0
  137. sqlalchemy/ext/serializer.py +185 -0
  138. sqlalchemy/future/__init__.py +16 -0
  139. sqlalchemy/future/engine.py +15 -0
  140. sqlalchemy/inspection.py +174 -0
  141. sqlalchemy/log.py +288 -0
  142. sqlalchemy/orm/__init__.py +171 -0
  143. sqlalchemy/orm/_orm_constructors.py +2661 -0
  144. sqlalchemy/orm/_typing.py +179 -0
  145. sqlalchemy/orm/attributes.py +2845 -0
  146. sqlalchemy/orm/base.py +971 -0
  147. sqlalchemy/orm/bulk_persistence.py +2135 -0
  148. sqlalchemy/orm/clsregistry.py +571 -0
  149. sqlalchemy/orm/collections.py +1627 -0
  150. sqlalchemy/orm/context.py +3334 -0
  151. sqlalchemy/orm/decl_api.py +2004 -0
  152. sqlalchemy/orm/decl_base.py +2192 -0
  153. sqlalchemy/orm/dependency.py +1302 -0
  154. sqlalchemy/orm/descriptor_props.py +1092 -0
  155. sqlalchemy/orm/dynamic.py +300 -0
  156. sqlalchemy/orm/evaluator.py +379 -0
  157. sqlalchemy/orm/events.py +3252 -0
  158. sqlalchemy/orm/exc.py +237 -0
  159. sqlalchemy/orm/identity.py +302 -0
  160. sqlalchemy/orm/instrumentation.py +754 -0
  161. sqlalchemy/orm/interfaces.py +1496 -0
  162. sqlalchemy/orm/loading.py +1686 -0
  163. sqlalchemy/orm/mapped_collection.py +557 -0
  164. sqlalchemy/orm/mapper.py +4444 -0
  165. sqlalchemy/orm/path_registry.py +809 -0
  166. sqlalchemy/orm/persistence.py +1788 -0
  167. sqlalchemy/orm/properties.py +935 -0
  168. sqlalchemy/orm/query.py +3459 -0
  169. sqlalchemy/orm/relationships.py +3508 -0
  170. sqlalchemy/orm/scoping.py +2148 -0
  171. sqlalchemy/orm/session.py +5280 -0
  172. sqlalchemy/orm/state.py +1168 -0
  173. sqlalchemy/orm/state_changes.py +196 -0
  174. sqlalchemy/orm/strategies.py +3470 -0
  175. sqlalchemy/orm/strategy_options.py +2568 -0
  176. sqlalchemy/orm/sync.py +164 -0
  177. sqlalchemy/orm/unitofwork.py +796 -0
  178. sqlalchemy/orm/util.py +2403 -0
  179. sqlalchemy/orm/writeonly.py +674 -0
  180. sqlalchemy/pool/__init__.py +44 -0
  181. sqlalchemy/pool/base.py +1524 -0
  182. sqlalchemy/pool/events.py +375 -0
  183. sqlalchemy/pool/impl.py +588 -0
  184. sqlalchemy/py.typed +0 -0
  185. sqlalchemy/schema.py +69 -0
  186. sqlalchemy/sql/__init__.py +145 -0
  187. sqlalchemy/sql/_dml_constructors.py +132 -0
  188. sqlalchemy/sql/_elements_constructors.py +1872 -0
  189. sqlalchemy/sql/_orm_types.py +20 -0
  190. sqlalchemy/sql/_py_util.py +75 -0
  191. sqlalchemy/sql/_selectable_constructors.py +763 -0
  192. sqlalchemy/sql/_typing.py +482 -0
  193. sqlalchemy/sql/annotation.py +587 -0
  194. sqlalchemy/sql/base.py +2293 -0
  195. sqlalchemy/sql/cache_key.py +1057 -0
  196. sqlalchemy/sql/coercions.py +1404 -0
  197. sqlalchemy/sql/compiler.py +8081 -0
  198. sqlalchemy/sql/crud.py +1752 -0
  199. sqlalchemy/sql/ddl.py +1444 -0
  200. sqlalchemy/sql/default_comparator.py +551 -0
  201. sqlalchemy/sql/dml.py +1850 -0
  202. sqlalchemy/sql/elements.py +5589 -0
  203. sqlalchemy/sql/events.py +458 -0
  204. sqlalchemy/sql/expression.py +159 -0
  205. sqlalchemy/sql/functions.py +2158 -0
  206. sqlalchemy/sql/lambdas.py +1442 -0
  207. sqlalchemy/sql/naming.py +209 -0
  208. sqlalchemy/sql/operators.py +2623 -0
  209. sqlalchemy/sql/roles.py +323 -0
  210. sqlalchemy/sql/schema.py +6222 -0
  211. sqlalchemy/sql/selectable.py +7265 -0
  212. sqlalchemy/sql/sqltypes.py +3930 -0
  213. sqlalchemy/sql/traversals.py +1024 -0
  214. sqlalchemy/sql/type_api.py +2368 -0
  215. sqlalchemy/sql/util.py +1485 -0
  216. sqlalchemy/sql/visitors.py +1164 -0
  217. sqlalchemy/testing/__init__.py +96 -0
  218. sqlalchemy/testing/assertions.py +994 -0
  219. sqlalchemy/testing/assertsql.py +520 -0
  220. sqlalchemy/testing/asyncio.py +135 -0
  221. sqlalchemy/testing/config.py +434 -0
  222. sqlalchemy/testing/engines.py +483 -0
  223. sqlalchemy/testing/entities.py +117 -0
  224. sqlalchemy/testing/exclusions.py +476 -0
  225. sqlalchemy/testing/fixtures/__init__.py +28 -0
  226. sqlalchemy/testing/fixtures/base.py +384 -0
  227. sqlalchemy/testing/fixtures/mypy.py +332 -0
  228. sqlalchemy/testing/fixtures/orm.py +227 -0
  229. sqlalchemy/testing/fixtures/sql.py +482 -0
  230. sqlalchemy/testing/pickleable.py +155 -0
  231. sqlalchemy/testing/plugin/__init__.py +6 -0
  232. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  233. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  234. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  235. sqlalchemy/testing/profiling.py +329 -0
  236. sqlalchemy/testing/provision.py +603 -0
  237. sqlalchemy/testing/requirements.py +1945 -0
  238. sqlalchemy/testing/schema.py +198 -0
  239. sqlalchemy/testing/suite/__init__.py +19 -0
  240. sqlalchemy/testing/suite/test_cte.py +237 -0
  241. sqlalchemy/testing/suite/test_ddl.py +389 -0
  242. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  243. sqlalchemy/testing/suite/test_dialect.py +776 -0
  244. sqlalchemy/testing/suite/test_insert.py +630 -0
  245. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  246. sqlalchemy/testing/suite/test_results.py +504 -0
  247. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  248. sqlalchemy/testing/suite/test_select.py +2010 -0
  249. sqlalchemy/testing/suite/test_sequence.py +317 -0
  250. sqlalchemy/testing/suite/test_types.py +2147 -0
  251. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  252. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  253. sqlalchemy/testing/util.py +535 -0
  254. sqlalchemy/testing/warnings.py +52 -0
  255. sqlalchemy/types.py +74 -0
  256. sqlalchemy/util/__init__.py +162 -0
  257. sqlalchemy/util/_collections.py +712 -0
  258. sqlalchemy/util/_concurrency_py3k.py +288 -0
  259. sqlalchemy/util/_has_cy.py +40 -0
  260. sqlalchemy/util/_py_collections.py +541 -0
  261. sqlalchemy/util/compat.py +421 -0
  262. sqlalchemy/util/concurrency.py +110 -0
  263. sqlalchemy/util/deprecations.py +401 -0
  264. sqlalchemy/util/langhelpers.py +2203 -0
  265. sqlalchemy/util/preloaded.py +150 -0
  266. sqlalchemy/util/queue.py +322 -0
  267. sqlalchemy/util/tool_support.py +201 -0
  268. sqlalchemy/util/topological.py +120 -0
  269. sqlalchemy/util/typing.py +734 -0
  270. sqlalchemy-2.0.47.dist-info/METADATA +243 -0
  271. sqlalchemy-2.0.47.dist-info/RECORD +274 -0
  272. sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
  273. sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
  274. sqlalchemy-2.0.47.dist-info/top_level.txt +1 -0
@@ -0,0 +1,630 @@
1
+ # testing/suite/test_insert.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 decimal import Decimal
10
+ import uuid
11
+
12
+ from . import testing
13
+ from .. import fixtures
14
+ from ..assertions import eq_
15
+ from ..config import requirements
16
+ from ..schema import Column
17
+ from ..schema import Table
18
+ from ... import Double
19
+ from ... import Float
20
+ from ... import Identity
21
+ from ... import Integer
22
+ from ... import literal
23
+ from ... import literal_column
24
+ from ... import Numeric
25
+ from ... import select
26
+ from ... import String
27
+ from ...types import LargeBinary
28
+ from ...types import UUID
29
+ from ...types import Uuid
30
+
31
+
32
+ class LastrowidTest(fixtures.TablesTest):
33
+ run_deletes = "each"
34
+
35
+ __backend__ = True
36
+
37
+ __requires__ = "implements_get_lastrowid", "autoincrement_insert"
38
+
39
+ @classmethod
40
+ def define_tables(cls, metadata):
41
+ Table(
42
+ "autoinc_pk",
43
+ metadata,
44
+ Column(
45
+ "id", Integer, primary_key=True, test_needs_autoincrement=True
46
+ ),
47
+ Column("data", String(50)),
48
+ implicit_returning=False,
49
+ )
50
+
51
+ Table(
52
+ "manual_pk",
53
+ metadata,
54
+ Column("id", Integer, primary_key=True, autoincrement=False),
55
+ Column("data", String(50)),
56
+ implicit_returning=False,
57
+ )
58
+
59
+ def _assert_round_trip(self, table, conn):
60
+ row = conn.execute(table.select()).first()
61
+ eq_(
62
+ row,
63
+ (
64
+ conn.dialect.default_sequence_base,
65
+ "some data",
66
+ ),
67
+ )
68
+
69
+ def test_autoincrement_on_insert(self, connection):
70
+ connection.execute(
71
+ self.tables.autoinc_pk.insert(), dict(data="some data")
72
+ )
73
+ self._assert_round_trip(self.tables.autoinc_pk, connection)
74
+
75
+ def test_last_inserted_id(self, connection):
76
+ r = connection.execute(
77
+ self.tables.autoinc_pk.insert(), dict(data="some data")
78
+ )
79
+ pk = connection.scalar(select(self.tables.autoinc_pk.c.id))
80
+ eq_(r.inserted_primary_key, (pk,))
81
+
82
+ @requirements.dbapi_lastrowid
83
+ def test_native_lastrowid_autoinc(self, connection):
84
+ r = connection.execute(
85
+ self.tables.autoinc_pk.insert(), dict(data="some data")
86
+ )
87
+ lastrowid = r.lastrowid
88
+ pk = connection.scalar(select(self.tables.autoinc_pk.c.id))
89
+ eq_(lastrowid, pk)
90
+
91
+
92
+ class InsertBehaviorTest(fixtures.TablesTest):
93
+ run_deletes = "each"
94
+ __backend__ = True
95
+
96
+ @classmethod
97
+ def define_tables(cls, metadata):
98
+ Table(
99
+ "autoinc_pk",
100
+ metadata,
101
+ Column(
102
+ "id", Integer, primary_key=True, test_needs_autoincrement=True
103
+ ),
104
+ Column("data", String(50)),
105
+ )
106
+ Table(
107
+ "manual_pk",
108
+ metadata,
109
+ Column("id", Integer, primary_key=True, autoincrement=False),
110
+ Column("data", String(50)),
111
+ )
112
+ Table(
113
+ "no_implicit_returning",
114
+ metadata,
115
+ Column(
116
+ "id", Integer, primary_key=True, test_needs_autoincrement=True
117
+ ),
118
+ Column("data", String(50)),
119
+ implicit_returning=False,
120
+ )
121
+ Table(
122
+ "includes_defaults",
123
+ metadata,
124
+ Column(
125
+ "id", Integer, primary_key=True, test_needs_autoincrement=True
126
+ ),
127
+ Column("data", String(50)),
128
+ Column("x", Integer, default=5),
129
+ Column(
130
+ "y",
131
+ Integer,
132
+ default=literal_column("2", type_=Integer) + literal(2),
133
+ ),
134
+ )
135
+
136
+ @testing.variation("style", ["plain", "return_defaults"])
137
+ @testing.variation("executemany", [True, False])
138
+ def test_no_results_for_non_returning_insert(
139
+ self, connection, style, executemany
140
+ ):
141
+ """test another INSERT issue found during #10453"""
142
+
143
+ table = self.tables.no_implicit_returning
144
+
145
+ stmt = table.insert()
146
+ if style.return_defaults:
147
+ stmt = stmt.return_defaults()
148
+
149
+ if executemany:
150
+ data = [
151
+ {"data": "d1"},
152
+ {"data": "d2"},
153
+ {"data": "d3"},
154
+ {"data": "d4"},
155
+ {"data": "d5"},
156
+ ]
157
+ else:
158
+ data = {"data": "d1"}
159
+
160
+ r = connection.execute(stmt, data)
161
+ assert not r.returns_rows
162
+
163
+ @requirements.autoincrement_insert
164
+ def test_autoclose_on_insert(self, connection):
165
+ r = connection.execute(
166
+ self.tables.autoinc_pk.insert(), dict(data="some data")
167
+ )
168
+ assert r._soft_closed
169
+ assert not r.closed
170
+ assert r.is_insert
171
+
172
+ # new as of I8091919d45421e3f53029b8660427f844fee0228; for the moment
173
+ # an insert where the PK was taken from a row that the dialect
174
+ # selected, as is the case for mssql/pyodbc, will still report
175
+ # returns_rows as true because there's a cursor description. in that
176
+ # case, the row had to have been consumed at least.
177
+ assert not r.returns_rows or r.fetchone() is None
178
+
179
+ @requirements.insert_returning
180
+ def test_autoclose_on_insert_implicit_returning(self, connection):
181
+ r = connection.execute(
182
+ # return_defaults() ensures RETURNING will be used,
183
+ # new in 2.0 as sqlite/mariadb offer both RETURNING and
184
+ # cursor.lastrowid
185
+ self.tables.autoinc_pk.insert().return_defaults(),
186
+ dict(data="some data"),
187
+ )
188
+ assert r._soft_closed
189
+ assert not r.closed
190
+ assert r.is_insert
191
+
192
+ # note we are experimenting with having this be True
193
+ # as of I8091919d45421e3f53029b8660427f844fee0228 .
194
+ # implicit returning has fetched the row, but it still is a
195
+ # "returns rows"
196
+ assert r.returns_rows
197
+
198
+ # and we should be able to fetchone() on it, we just get no row
199
+ eq_(r.fetchone(), None)
200
+
201
+ # and the keys, etc.
202
+ eq_(r.keys(), ["id"])
203
+
204
+ # but the dialect took in the row already. not really sure
205
+ # what the best behavior is.
206
+
207
+ @requirements.empty_inserts
208
+ def test_empty_insert(self, connection):
209
+ r = connection.execute(self.tables.autoinc_pk.insert())
210
+ assert r._soft_closed
211
+ assert not r.closed
212
+
213
+ r = connection.execute(
214
+ self.tables.autoinc_pk.select().where(
215
+ self.tables.autoinc_pk.c.id != None
216
+ )
217
+ )
218
+ eq_(len(r.all()), 1)
219
+
220
+ @requirements.empty_inserts_executemany
221
+ def test_empty_insert_multiple(self, connection):
222
+ r = connection.execute(self.tables.autoinc_pk.insert(), [{}, {}, {}])
223
+ assert r._soft_closed
224
+ assert not r.closed
225
+
226
+ r = connection.execute(
227
+ self.tables.autoinc_pk.select().where(
228
+ self.tables.autoinc_pk.c.id != None
229
+ )
230
+ )
231
+
232
+ eq_(len(r.all()), 3)
233
+
234
+ @requirements.insert_from_select
235
+ def test_insert_from_select_autoinc(self, connection):
236
+ src_table = self.tables.manual_pk
237
+ dest_table = self.tables.autoinc_pk
238
+ connection.execute(
239
+ src_table.insert(),
240
+ [
241
+ dict(id=1, data="data1"),
242
+ dict(id=2, data="data2"),
243
+ dict(id=3, data="data3"),
244
+ ],
245
+ )
246
+
247
+ result = connection.execute(
248
+ dest_table.insert().from_select(
249
+ ("data",),
250
+ select(src_table.c.data).where(
251
+ src_table.c.data.in_(["data2", "data3"])
252
+ ),
253
+ )
254
+ )
255
+
256
+ eq_(result.inserted_primary_key, (None,))
257
+
258
+ result = connection.execute(
259
+ select(dest_table.c.data).order_by(dest_table.c.data)
260
+ )
261
+ eq_(result.fetchall(), [("data2",), ("data3",)])
262
+
263
+ @requirements.insert_from_select
264
+ def test_insert_from_select_autoinc_no_rows(self, connection):
265
+ src_table = self.tables.manual_pk
266
+ dest_table = self.tables.autoinc_pk
267
+
268
+ result = connection.execute(
269
+ dest_table.insert().from_select(
270
+ ("data",),
271
+ select(src_table.c.data).where(
272
+ src_table.c.data.in_(["data2", "data3"])
273
+ ),
274
+ )
275
+ )
276
+ eq_(result.inserted_primary_key, (None,))
277
+
278
+ result = connection.execute(
279
+ select(dest_table.c.data).order_by(dest_table.c.data)
280
+ )
281
+
282
+ eq_(result.fetchall(), [])
283
+
284
+ @requirements.insert_from_select
285
+ def test_insert_from_select(self, connection):
286
+ table = self.tables.manual_pk
287
+ connection.execute(
288
+ table.insert(),
289
+ [
290
+ dict(id=1, data="data1"),
291
+ dict(id=2, data="data2"),
292
+ dict(id=3, data="data3"),
293
+ ],
294
+ )
295
+
296
+ connection.execute(
297
+ table.insert()
298
+ .inline()
299
+ .from_select(
300
+ ("id", "data"),
301
+ select(table.c.id + 5, table.c.data).where(
302
+ table.c.data.in_(["data2", "data3"])
303
+ ),
304
+ )
305
+ )
306
+
307
+ eq_(
308
+ connection.execute(
309
+ select(table.c.data).order_by(table.c.data)
310
+ ).fetchall(),
311
+ [("data1",), ("data2",), ("data2",), ("data3",), ("data3",)],
312
+ )
313
+
314
+ @requirements.insert_from_select
315
+ def test_insert_from_select_with_defaults(self, connection):
316
+ table = self.tables.includes_defaults
317
+ connection.execute(
318
+ table.insert(),
319
+ [
320
+ dict(id=1, data="data1"),
321
+ dict(id=2, data="data2"),
322
+ dict(id=3, data="data3"),
323
+ ],
324
+ )
325
+
326
+ connection.execute(
327
+ table.insert()
328
+ .inline()
329
+ .from_select(
330
+ ("id", "data"),
331
+ select(table.c.id + 5, table.c.data).where(
332
+ table.c.data.in_(["data2", "data3"])
333
+ ),
334
+ )
335
+ )
336
+
337
+ eq_(
338
+ connection.execute(
339
+ select(table).order_by(table.c.data, table.c.id)
340
+ ).fetchall(),
341
+ [
342
+ (1, "data1", 5, 4),
343
+ (2, "data2", 5, 4),
344
+ (7, "data2", 5, 4),
345
+ (3, "data3", 5, 4),
346
+ (8, "data3", 5, 4),
347
+ ],
348
+ )
349
+
350
+
351
+ class ReturningTest(fixtures.TablesTest):
352
+ run_create_tables = "each"
353
+ __requires__ = "insert_returning", "autoincrement_insert"
354
+ __backend__ = True
355
+
356
+ def _assert_round_trip(self, table, conn):
357
+ row = conn.execute(table.select()).first()
358
+ eq_(
359
+ row,
360
+ (
361
+ conn.dialect.default_sequence_base,
362
+ "some data",
363
+ ),
364
+ )
365
+
366
+ @classmethod
367
+ def define_tables(cls, metadata):
368
+ Table(
369
+ "autoinc_pk",
370
+ metadata,
371
+ Column(
372
+ "id", Integer, primary_key=True, test_needs_autoincrement=True
373
+ ),
374
+ Column("data", String(50)),
375
+ )
376
+
377
+ @requirements.fetch_rows_post_commit
378
+ def test_explicit_returning_pk_autocommit(self, connection):
379
+ table = self.tables.autoinc_pk
380
+ r = connection.execute(
381
+ table.insert().returning(table.c.id), dict(data="some data")
382
+ )
383
+ pk = r.first()[0]
384
+ fetched_pk = connection.scalar(select(table.c.id))
385
+ eq_(fetched_pk, pk)
386
+
387
+ def test_explicit_returning_pk_no_autocommit(self, connection):
388
+ table = self.tables.autoinc_pk
389
+ r = connection.execute(
390
+ table.insert().returning(table.c.id), dict(data="some data")
391
+ )
392
+
393
+ pk = r.first()[0]
394
+ fetched_pk = connection.scalar(select(table.c.id))
395
+ eq_(fetched_pk, pk)
396
+
397
+ def test_autoincrement_on_insert_implicit_returning(self, connection):
398
+ connection.execute(
399
+ self.tables.autoinc_pk.insert(), dict(data="some data")
400
+ )
401
+ self._assert_round_trip(self.tables.autoinc_pk, connection)
402
+
403
+ def test_last_inserted_id_implicit_returning(self, connection):
404
+ r = connection.execute(
405
+ self.tables.autoinc_pk.insert(), dict(data="some data")
406
+ )
407
+ pk = connection.scalar(select(self.tables.autoinc_pk.c.id))
408
+ eq_(r.inserted_primary_key, (pk,))
409
+
410
+ @requirements.insert_executemany_returning
411
+ def test_insertmanyvalues_returning(self, connection):
412
+ r = connection.execute(
413
+ self.tables.autoinc_pk.insert().returning(
414
+ self.tables.autoinc_pk.c.id
415
+ ),
416
+ [
417
+ {"data": "d1"},
418
+ {"data": "d2"},
419
+ {"data": "d3"},
420
+ {"data": "d4"},
421
+ {"data": "d5"},
422
+ ],
423
+ )
424
+ rall = r.all()
425
+
426
+ pks = connection.execute(select(self.tables.autoinc_pk.c.id))
427
+
428
+ eq_(rall, pks.all())
429
+
430
+ @testing.combinations(
431
+ (Double(), 8.5514716, True),
432
+ (
433
+ Double(53),
434
+ 8.5514716,
435
+ True,
436
+ testing.requires.float_or_double_precision_behaves_generically,
437
+ ),
438
+ (Float(), 8.5514, True),
439
+ (
440
+ Float(8),
441
+ 8.5514,
442
+ True,
443
+ testing.requires.float_or_double_precision_behaves_generically,
444
+ ),
445
+ (
446
+ Numeric(precision=15, scale=12, asdecimal=False),
447
+ 8.5514716,
448
+ True,
449
+ testing.requires.literal_float_coercion,
450
+ ),
451
+ (
452
+ Numeric(precision=15, scale=12, asdecimal=True),
453
+ Decimal("8.5514716"),
454
+ False,
455
+ ),
456
+ argnames="type_,value,do_rounding",
457
+ )
458
+ @testing.variation("sort_by_parameter_order", [True, False])
459
+ @testing.variation("multiple_rows", [True, False])
460
+ def test_insert_w_floats(
461
+ self,
462
+ connection,
463
+ metadata,
464
+ sort_by_parameter_order,
465
+ type_,
466
+ value,
467
+ do_rounding,
468
+ multiple_rows,
469
+ ):
470
+ """test #9701.
471
+
472
+ this tests insertmanyvalues as well as decimal / floating point
473
+ RETURNING types
474
+
475
+ """
476
+
477
+ t = Table(
478
+ # Oracle backends seems to be getting confused if
479
+ # this table is named the same as the one
480
+ # in test_imv_returning_datatypes. use a different name
481
+ "f_t",
482
+ metadata,
483
+ Column("id", Integer, Identity(), primary_key=True),
484
+ Column("value", type_),
485
+ )
486
+
487
+ t.create(connection)
488
+
489
+ result = connection.execute(
490
+ t.insert().returning(
491
+ t.c.id,
492
+ t.c.value,
493
+ sort_by_parameter_order=bool(sort_by_parameter_order),
494
+ ),
495
+ (
496
+ [{"value": value} for i in range(10)]
497
+ if multiple_rows
498
+ else {"value": value}
499
+ ),
500
+ )
501
+
502
+ if multiple_rows:
503
+ i_range = range(1, 11)
504
+ else:
505
+ i_range = range(1, 2)
506
+
507
+ # we want to test only that we are getting floating points back
508
+ # with some degree of the original value maintained, that it is not
509
+ # being truncated to an integer. there's too much variation in how
510
+ # drivers return floats, which should not be relied upon to be
511
+ # exact, for us to just compare as is (works for PG drivers but not
512
+ # others) so we use rounding here. There's precedent for this
513
+ # in suite/test_types.py::NumericTest as well
514
+
515
+ if do_rounding:
516
+ eq_(
517
+ {(id_, round(val_, 5)) for id_, val_ in result},
518
+ {(id_, round(value, 5)) for id_ in i_range},
519
+ )
520
+
521
+ eq_(
522
+ {
523
+ round(val_, 5)
524
+ for val_ in connection.scalars(select(t.c.value))
525
+ },
526
+ {round(value, 5)},
527
+ )
528
+ else:
529
+ eq_(
530
+ set(result),
531
+ {(id_, value) for id_ in i_range},
532
+ )
533
+
534
+ eq_(
535
+ set(connection.scalars(select(t.c.value))),
536
+ {value},
537
+ )
538
+
539
+ @testing.combinations(
540
+ (
541
+ "non_native_uuid",
542
+ Uuid(native_uuid=False),
543
+ uuid.uuid4(),
544
+ ),
545
+ (
546
+ "non_native_uuid_str",
547
+ Uuid(as_uuid=False, native_uuid=False),
548
+ str(uuid.uuid4()),
549
+ ),
550
+ (
551
+ "generic_native_uuid",
552
+ Uuid(native_uuid=True),
553
+ uuid.uuid4(),
554
+ testing.requires.uuid_data_type,
555
+ ),
556
+ (
557
+ "generic_native_uuid_str",
558
+ Uuid(as_uuid=False, native_uuid=True),
559
+ str(uuid.uuid4()),
560
+ testing.requires.uuid_data_type,
561
+ ),
562
+ ("UUID", UUID(), uuid.uuid4(), testing.requires.uuid_data_type),
563
+ (
564
+ "LargeBinary1",
565
+ LargeBinary(),
566
+ b"this is binary",
567
+ ),
568
+ ("LargeBinary2", LargeBinary(), b"7\xe7\x9f"),
569
+ argnames="type_,value",
570
+ id_="iaa",
571
+ )
572
+ @testing.variation("sort_by_parameter_order", [True, False])
573
+ @testing.variation("multiple_rows", [True, False])
574
+ @testing.requires.insert_returning
575
+ def test_imv_returning_datatypes(
576
+ self,
577
+ connection,
578
+ metadata,
579
+ sort_by_parameter_order,
580
+ type_,
581
+ value,
582
+ multiple_rows,
583
+ ):
584
+ """test #9739, #9808 (similar to #9701).
585
+
586
+ this tests insertmanyvalues in conjunction with various datatypes.
587
+
588
+ These tests are particularly for the asyncpg driver which needs
589
+ most types to be explicitly cast for the new IMV format
590
+
591
+ """
592
+ t = Table(
593
+ "d_t",
594
+ metadata,
595
+ Column("id", Integer, Identity(), primary_key=True),
596
+ Column("value", type_),
597
+ )
598
+
599
+ t.create(connection)
600
+
601
+ result = connection.execute(
602
+ t.insert().returning(
603
+ t.c.id,
604
+ t.c.value,
605
+ sort_by_parameter_order=bool(sort_by_parameter_order),
606
+ ),
607
+ (
608
+ [{"value": value} for i in range(10)]
609
+ if multiple_rows
610
+ else {"value": value}
611
+ ),
612
+ )
613
+
614
+ if multiple_rows:
615
+ i_range = range(1, 11)
616
+ else:
617
+ i_range = range(1, 2)
618
+
619
+ eq_(
620
+ set(result),
621
+ {(id_, value) for id_ in i_range},
622
+ )
623
+
624
+ eq_(
625
+ set(connection.scalars(select(t.c.value))),
626
+ {value},
627
+ )
628
+
629
+
630
+ __all__ = ("LastrowidTest", "InsertBehaviorTest", "ReturningTest")