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.
- sqlalchemy/__init__.py +283 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +184 -0
- sqlalchemy/connectors/asyncio.py +429 -0
- sqlalchemy/connectors/pyodbc.py +250 -0
- sqlalchemy/cyextension/__init__.py +6 -0
- sqlalchemy/cyextension/collections.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/collections.pyx +409 -0
- sqlalchemy/cyextension/immutabledict.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/immutabledict.pxd +8 -0
- sqlalchemy/cyextension/immutabledict.pyx +133 -0
- sqlalchemy/cyextension/processors.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/processors.pyx +68 -0
- sqlalchemy/cyextension/resultproxy.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/resultproxy.pyx +102 -0
- sqlalchemy/cyextension/util.cp313t-win_amd64.pyd +0 -0
- sqlalchemy/cyextension/util.pyx +90 -0
- sqlalchemy/dialects/__init__.py +62 -0
- sqlalchemy/dialects/_typing.py +30 -0
- sqlalchemy/dialects/mssql/__init__.py +88 -0
- sqlalchemy/dialects/mssql/aioodbc.py +63 -0
- sqlalchemy/dialects/mssql/base.py +4093 -0
- sqlalchemy/dialects/mssql/information_schema.py +285 -0
- sqlalchemy/dialects/mssql/json.py +129 -0
- sqlalchemy/dialects/mssql/provision.py +185 -0
- sqlalchemy/dialects/mssql/pymssql.py +126 -0
- sqlalchemy/dialects/mssql/pyodbc.py +760 -0
- sqlalchemy/dialects/mysql/__init__.py +104 -0
- sqlalchemy/dialects/mysql/aiomysql.py +250 -0
- sqlalchemy/dialects/mysql/asyncmy.py +231 -0
- sqlalchemy/dialects/mysql/base.py +3949 -0
- sqlalchemy/dialects/mysql/cymysql.py +106 -0
- sqlalchemy/dialects/mysql/dml.py +225 -0
- sqlalchemy/dialects/mysql/enumerated.py +282 -0
- sqlalchemy/dialects/mysql/expression.py +146 -0
- sqlalchemy/dialects/mysql/json.py +91 -0
- sqlalchemy/dialects/mysql/mariadb.py +72 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
- sqlalchemy/dialects/mysql/mysqldb.py +314 -0
- sqlalchemy/dialects/mysql/provision.py +153 -0
- sqlalchemy/dialects/mysql/pymysql.py +158 -0
- sqlalchemy/dialects/mysql/pyodbc.py +157 -0
- sqlalchemy/dialects/mysql/reflection.py +727 -0
- sqlalchemy/dialects/mysql/reserved_words.py +570 -0
- sqlalchemy/dialects/mysql/types.py +835 -0
- sqlalchemy/dialects/oracle/__init__.py +81 -0
- sqlalchemy/dialects/oracle/base.py +3802 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/oracledb.py +941 -0
- sqlalchemy/dialects/oracle/provision.py +297 -0
- sqlalchemy/dialects/oracle/types.py +316 -0
- sqlalchemy/dialects/oracle/vector.py +365 -0
- sqlalchemy/dialects/postgresql/__init__.py +167 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
- sqlalchemy/dialects/postgresql/array.py +519 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
- sqlalchemy/dialects/postgresql/base.py +5378 -0
- sqlalchemy/dialects/postgresql/dml.py +339 -0
- sqlalchemy/dialects/postgresql/ext.py +540 -0
- sqlalchemy/dialects/postgresql/hstore.py +406 -0
- sqlalchemy/dialects/postgresql/json.py +404 -0
- sqlalchemy/dialects/postgresql/named_types.py +524 -0
- sqlalchemy/dialects/postgresql/operators.py +129 -0
- sqlalchemy/dialects/postgresql/pg8000.py +669 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
- sqlalchemy/dialects/postgresql/provision.py +183 -0
- sqlalchemy/dialects/postgresql/psycopg.py +862 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
- sqlalchemy/dialects/postgresql/ranges.py +1031 -0
- sqlalchemy/dialects/postgresql/types.py +313 -0
- sqlalchemy/dialects/sqlite/__init__.py +57 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
- sqlalchemy/dialects/sqlite/base.py +3056 -0
- sqlalchemy/dialects/sqlite/dml.py +263 -0
- sqlalchemy/dialects/sqlite/json.py +92 -0
- sqlalchemy/dialects/sqlite/provision.py +229 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_py_processors.py +136 -0
- sqlalchemy/engine/_py_row.py +128 -0
- sqlalchemy/engine/_py_util.py +74 -0
- sqlalchemy/engine/base.py +3390 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +893 -0
- sqlalchemy/engine/cursor.py +2298 -0
- sqlalchemy/engine/default.py +2394 -0
- sqlalchemy/engine/events.py +965 -0
- sqlalchemy/engine/interfaces.py +3471 -0
- sqlalchemy/engine/mock.py +134 -0
- sqlalchemy/engine/processors.py +61 -0
- sqlalchemy/engine/reflection.py +2102 -0
- sqlalchemy/engine/result.py +2399 -0
- sqlalchemy/engine/row.py +400 -0
- sqlalchemy/engine/strategies.py +16 -0
- sqlalchemy/engine/url.py +924 -0
- sqlalchemy/engine/util.py +167 -0
- sqlalchemy/event/__init__.py +26 -0
- sqlalchemy/event/api.py +220 -0
- sqlalchemy/event/attr.py +676 -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 +832 -0
- sqlalchemy/ext/__init__.py +11 -0
- sqlalchemy/ext/associationproxy.py +2027 -0
- sqlalchemy/ext/asyncio/__init__.py +25 -0
- sqlalchemy/ext/asyncio/base.py +281 -0
- sqlalchemy/ext/asyncio/engine.py +1471 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +965 -0
- sqlalchemy/ext/asyncio/scoping.py +1599 -0
- sqlalchemy/ext/asyncio/session.py +1947 -0
- sqlalchemy/ext/automap.py +1701 -0
- sqlalchemy/ext/baked.py +570 -0
- sqlalchemy/ext/compiler.py +600 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +564 -0
- sqlalchemy/ext/horizontal_shard.py +478 -0
- sqlalchemy/ext/hybrid.py +1535 -0
- sqlalchemy/ext/indexable.py +364 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1085 -0
- sqlalchemy/ext/mypy/__init__.py +6 -0
- sqlalchemy/ext/mypy/apply.py +324 -0
- sqlalchemy/ext/mypy/decl_class.py +515 -0
- sqlalchemy/ext/mypy/infer.py +590 -0
- sqlalchemy/ext/mypy/names.py +335 -0
- sqlalchemy/ext/mypy/plugin.py +303 -0
- sqlalchemy/ext/mypy/util.py +357 -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 +288 -0
- sqlalchemy/orm/__init__.py +171 -0
- sqlalchemy/orm/_orm_constructors.py +2661 -0
- sqlalchemy/orm/_typing.py +179 -0
- sqlalchemy/orm/attributes.py +2845 -0
- sqlalchemy/orm/base.py +971 -0
- sqlalchemy/orm/bulk_persistence.py +2135 -0
- sqlalchemy/orm/clsregistry.py +571 -0
- sqlalchemy/orm/collections.py +1627 -0
- sqlalchemy/orm/context.py +3334 -0
- sqlalchemy/orm/decl_api.py +2004 -0
- sqlalchemy/orm/decl_base.py +2192 -0
- sqlalchemy/orm/dependency.py +1302 -0
- sqlalchemy/orm/descriptor_props.py +1092 -0
- sqlalchemy/orm/dynamic.py +300 -0
- sqlalchemy/orm/evaluator.py +379 -0
- sqlalchemy/orm/events.py +3252 -0
- sqlalchemy/orm/exc.py +237 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +754 -0
- sqlalchemy/orm/interfaces.py +1496 -0
- sqlalchemy/orm/loading.py +1686 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4444 -0
- sqlalchemy/orm/path_registry.py +809 -0
- sqlalchemy/orm/persistence.py +1788 -0
- sqlalchemy/orm/properties.py +935 -0
- sqlalchemy/orm/query.py +3459 -0
- sqlalchemy/orm/relationships.py +3508 -0
- sqlalchemy/orm/scoping.py +2148 -0
- sqlalchemy/orm/session.py +5280 -0
- sqlalchemy/orm/state.py +1168 -0
- sqlalchemy/orm/state_changes.py +196 -0
- sqlalchemy/orm/strategies.py +3470 -0
- sqlalchemy/orm/strategy_options.py +2568 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +796 -0
- sqlalchemy/orm/util.py +2403 -0
- sqlalchemy/orm/writeonly.py +674 -0
- sqlalchemy/pool/__init__.py +44 -0
- sqlalchemy/pool/base.py +1524 -0
- sqlalchemy/pool/events.py +375 -0
- sqlalchemy/pool/impl.py +588 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +69 -0
- sqlalchemy/sql/__init__.py +145 -0
- sqlalchemy/sql/_dml_constructors.py +132 -0
- sqlalchemy/sql/_elements_constructors.py +1872 -0
- sqlalchemy/sql/_orm_types.py +20 -0
- sqlalchemy/sql/_py_util.py +75 -0
- sqlalchemy/sql/_selectable_constructors.py +763 -0
- sqlalchemy/sql/_typing.py +482 -0
- sqlalchemy/sql/annotation.py +587 -0
- sqlalchemy/sql/base.py +2293 -0
- sqlalchemy/sql/cache_key.py +1057 -0
- sqlalchemy/sql/coercions.py +1404 -0
- sqlalchemy/sql/compiler.py +8081 -0
- sqlalchemy/sql/crud.py +1752 -0
- sqlalchemy/sql/ddl.py +1444 -0
- sqlalchemy/sql/default_comparator.py +551 -0
- sqlalchemy/sql/dml.py +1850 -0
- sqlalchemy/sql/elements.py +5589 -0
- sqlalchemy/sql/events.py +458 -0
- sqlalchemy/sql/expression.py +159 -0
- sqlalchemy/sql/functions.py +2158 -0
- sqlalchemy/sql/lambdas.py +1442 -0
- sqlalchemy/sql/naming.py +209 -0
- sqlalchemy/sql/operators.py +2623 -0
- sqlalchemy/sql/roles.py +323 -0
- sqlalchemy/sql/schema.py +6222 -0
- sqlalchemy/sql/selectable.py +7265 -0
- sqlalchemy/sql/sqltypes.py +3930 -0
- sqlalchemy/sql/traversals.py +1024 -0
- sqlalchemy/sql/type_api.py +2368 -0
- sqlalchemy/sql/util.py +1485 -0
- sqlalchemy/sql/visitors.py +1164 -0
- sqlalchemy/testing/__init__.py +96 -0
- sqlalchemy/testing/assertions.py +994 -0
- sqlalchemy/testing/assertsql.py +520 -0
- sqlalchemy/testing/asyncio.py +135 -0
- sqlalchemy/testing/config.py +434 -0
- sqlalchemy/testing/engines.py +483 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +476 -0
- sqlalchemy/testing/fixtures/__init__.py +28 -0
- sqlalchemy/testing/fixtures/base.py +384 -0
- sqlalchemy/testing/fixtures/mypy.py +332 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +482 -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 +603 -0
- sqlalchemy/testing/requirements.py +1945 -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 +389 -0
- sqlalchemy/testing/suite/test_deprecations.py +153 -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 +504 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +2010 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_types.py +2147 -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 +74 -0
- sqlalchemy/util/__init__.py +162 -0
- sqlalchemy/util/_collections.py +712 -0
- sqlalchemy/util/_concurrency_py3k.py +288 -0
- sqlalchemy/util/_has_cy.py +40 -0
- sqlalchemy/util/_py_collections.py +541 -0
- sqlalchemy/util/compat.py +421 -0
- sqlalchemy/util/concurrency.py +110 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2203 -0
- sqlalchemy/util/preloaded.py +150 -0
- sqlalchemy/util/queue.py +322 -0
- sqlalchemy/util/tool_support.py +201 -0
- sqlalchemy/util/topological.py +120 -0
- sqlalchemy/util/typing.py +734 -0
- sqlalchemy-2.0.47.dist-info/METADATA +243 -0
- sqlalchemy-2.0.47.dist-info/RECORD +274 -0
- sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
- sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
- 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")
|