crate 2.2.0__tar.gz → 2.2.1b2__tar.gz
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.
- {crate-2.2.0 → crate-2.2.1b2}/PKG-INFO +1 -1
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/test_cursor.py +132 -0
- {crate-2.2.0 → crate-2.2.1b2}/.gitignore +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/CHANGES.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/CONTRIBUTING.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/DEVELOP.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/LICENSE +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/NOTICE +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/README.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/.gitignore +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/Makefile +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/_extra/robots.txt +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/blobs.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/build.json +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/by-example/blob.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/by-example/client.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/by-example/connection.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/by-example/cursor.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/by-example/http.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/by-example/https.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/by-example/index.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/conf.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/connect.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/data-types.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/docutils.conf +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/getting-started.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/index-all.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/index.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/other-options.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/query.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/docs/requirements.txt +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/examples/README.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/pyproject.toml +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/__init__.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/import/test_a.json +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/mappings/locations.sql +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/pki/cacert_invalid.pem +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/pki/cacert_valid.pem +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/pki/client_invalid.pem +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/pki/client_valid.pem +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/pki/readme.rst +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/pki/server_valid.pem +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/assets/settings/test_a.json +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/__init__.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/settings.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/test_blob.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/test_connection.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/test_exceptions.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/test_http.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/test_serialization.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/client/test_utils.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/conftest.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/test_docs.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/testing/__init__.py +0 -0
- {crate-2.2.0 → crate-2.2.1b2}/tests/testing/test_layer.py +0 -0
|
@@ -125,6 +125,92 @@ def test_cursor_executemany(mocked_connection):
|
|
|
125
125
|
assert response["results"] == result
|
|
126
126
|
|
|
127
127
|
|
|
128
|
+
def test_executemany_with_named_params(mocked_connection):
|
|
129
|
+
"""
|
|
130
|
+
Verify that executemany() translates pyformat %(name)s placeholders to
|
|
131
|
+
positional $N markers and converts each dict row to a positional list.
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
response = {
|
|
135
|
+
"col_types": [],
|
|
136
|
+
"cols": [],
|
|
137
|
+
"duration": 123,
|
|
138
|
+
"results": [{"rowcount": 1}, {"rowcount": 1}],
|
|
139
|
+
}
|
|
140
|
+
with mock.patch.object(
|
|
141
|
+
mocked_connection.client, "sql", return_value=response
|
|
142
|
+
):
|
|
143
|
+
cursor = mocked_connection.cursor()
|
|
144
|
+
cursor.executemany(
|
|
145
|
+
"INSERT INTO characters (name, age) VALUES (%(name)s, %(age)s)",
|
|
146
|
+
[
|
|
147
|
+
{"name": "Arthur", "age": 42},
|
|
148
|
+
{"name": "Bill", "age": 35},
|
|
149
|
+
],
|
|
150
|
+
)
|
|
151
|
+
sql, _params, bulk_args = mocked_connection.client.sql.call_args[0]
|
|
152
|
+
assert sql == "INSERT INTO characters (name, age) VALUES ($1, $2)"
|
|
153
|
+
assert bulk_args == [["Arthur", 42], ["Bill", 35]]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_executemany_with_named_params_missing_key(mocked_connection):
|
|
157
|
+
"""
|
|
158
|
+
Verify that executemany() raises ProgrammingError when a row is missing a
|
|
159
|
+
key that appears as a placeholder in the SQL.
|
|
160
|
+
"""
|
|
161
|
+
cursor = mocked_connection.cursor()
|
|
162
|
+
with pytest.raises(
|
|
163
|
+
ProgrammingError, match="Named parameter 'age' not found"
|
|
164
|
+
):
|
|
165
|
+
cursor.executemany(
|
|
166
|
+
"INSERT INTO characters (name, age) VALUES (%(name)s, %(age)s)",
|
|
167
|
+
[
|
|
168
|
+
{"name": "Arthur", "age": 42},
|
|
169
|
+
{"name": "Bill"}, # missing 'age'
|
|
170
|
+
],
|
|
171
|
+
)
|
|
172
|
+
mocked_connection.client.sql.assert_not_called()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_executemany_with_named_params_repeated(mocked_connection):
|
|
176
|
+
"""
|
|
177
|
+
Verify that a placeholder name used multiple times in the SQL maps to the
|
|
178
|
+
same $N position in every occurrence, and the value appears only once in
|
|
179
|
+
each row's positional list.
|
|
180
|
+
"""
|
|
181
|
+
response = {
|
|
182
|
+
"col_types": [],
|
|
183
|
+
"cols": [],
|
|
184
|
+
"duration": 123,
|
|
185
|
+
"results": [{"rowcount": 1}, {"rowcount": 1}],
|
|
186
|
+
}
|
|
187
|
+
with mock.patch.object(
|
|
188
|
+
mocked_connection.client, "sql", return_value=response
|
|
189
|
+
):
|
|
190
|
+
cursor = mocked_connection.cursor()
|
|
191
|
+
cursor.executemany(
|
|
192
|
+
"INSERT INTO t (a, b) VALUES (%(x)s, %(x)s)",
|
|
193
|
+
[{"x": 1}, {"x": 2}],
|
|
194
|
+
)
|
|
195
|
+
sql, _params, bulk_args = mocked_connection.client.sql.call_args[0]
|
|
196
|
+
assert sql == "INSERT INTO t (a, b) VALUES ($1, $1)"
|
|
197
|
+
assert bulk_args == [[1], [2]]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_executemany_with_mixed_param_types(mocked_connection):
|
|
201
|
+
"""
|
|
202
|
+
Verify that executemany() raises a clear ProgrammingError when the
|
|
203
|
+
parameter sequence mixes dicts and non-dicts while the SQL uses pyformat.
|
|
204
|
+
"""
|
|
205
|
+
cursor = mocked_connection.cursor()
|
|
206
|
+
with pytest.raises(ProgrammingError, match="requires all parameter rows"):
|
|
207
|
+
cursor.executemany(
|
|
208
|
+
"INSERT INTO characters (name) VALUES (%(name)s)",
|
|
209
|
+
[{"name": "Arthur"}, ["Trillian"]], # second row is a list
|
|
210
|
+
)
|
|
211
|
+
mocked_connection.client.sql.assert_not_called()
|
|
212
|
+
|
|
213
|
+
|
|
128
214
|
def test_create_with_timezone_as_datetime_object(mocked_connection):
|
|
129
215
|
"""
|
|
130
216
|
The cursor can return timezone-aware `datetime` objects when requested.
|
|
@@ -243,6 +329,34 @@ def test_execute_with_bulk_args(mocked_connection):
|
|
|
243
329
|
mocked_connection.client.sql.assert_called_once_with(statement, None, [[1]])
|
|
244
330
|
|
|
245
331
|
|
|
332
|
+
def test_execute_with_pyformat_sql_and_bulk_parameters(mocked_connection):
|
|
333
|
+
"""
|
|
334
|
+
cursor.execute() converts %(name)s SQL to $N when bulk_parameters is
|
|
335
|
+
provided. Rows are already positional; only the SQL needs conversion.
|
|
336
|
+
"""
|
|
337
|
+
cursor = mocked_connection.cursor()
|
|
338
|
+
sql = "INSERT INTO t (id, val) VALUES (%(id)s, %(val)s)"
|
|
339
|
+
bulk = [[1, "hello"], [2, "world"]]
|
|
340
|
+
cursor.execute(sql, bulk_parameters=bulk)
|
|
341
|
+
mocked_connection.client.sql.assert_called_once_with(
|
|
342
|
+
"INSERT INTO t (id, val) VALUES ($1, $2)", None, bulk
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def test_execute_with_pyformat_sql_and_bulk_parameters_no_placeholders(
|
|
347
|
+
mocked_connection,
|
|
348
|
+
):
|
|
349
|
+
"""
|
|
350
|
+
SQL without %(name)s placeholders is passed through unchanged
|
|
351
|
+
even when bulk_parameters is provided.
|
|
352
|
+
"""
|
|
353
|
+
cursor = mocked_connection.cursor()
|
|
354
|
+
sql = "INSERT INTO t (id, val) VALUES (?, ?)"
|
|
355
|
+
bulk = [[1, "hello"], [2, "world"]]
|
|
356
|
+
cursor.execute(sql, bulk_parameters=bulk)
|
|
357
|
+
mocked_connection.client.sql.assert_called_once_with(sql, None, bulk)
|
|
358
|
+
|
|
359
|
+
|
|
246
360
|
def test_execute_custom_converter(mocked_connection):
|
|
247
361
|
"""
|
|
248
362
|
Verify that a custom converter is correctly applied when passed to a cursor.
|
|
@@ -565,6 +679,24 @@ def test_execute_with_named_params_missing(mocked_connection):
|
|
|
565
679
|
mocked_connection.client.sql.assert_not_called()
|
|
566
680
|
|
|
567
681
|
|
|
682
|
+
def test_execute_with_named_params_non_identifier_keys(mocked_connection):
|
|
683
|
+
"""
|
|
684
|
+
Verify that %(name)s placeholders whose name contains characters outside
|
|
685
|
+
[a-zA-Z0-9_] are still converted to positional $N markers.
|
|
686
|
+
|
|
687
|
+
"""
|
|
688
|
+
cursor = mocked_connection.cursor()
|
|
689
|
+
|
|
690
|
+
cursor.execute(
|
|
691
|
+
"UPDATE characters SET data['x'] = %(data['x'])s WHERE name = %(name)s",
|
|
692
|
+
{"data['x']": 42, "name": "Berlin"},
|
|
693
|
+
)
|
|
694
|
+
sql, args, _ = mocked_connection.client.sql.call_args[0]
|
|
695
|
+
assert "%" not in sql
|
|
696
|
+
assert sql == "UPDATE characters SET data['x'] = $1 WHERE name = $2"
|
|
697
|
+
assert args == [42, "Berlin"]
|
|
698
|
+
|
|
699
|
+
|
|
568
700
|
def test_cursor_close(mocked_connection):
|
|
569
701
|
"""
|
|
570
702
|
Verify that a cursor is not closed if not specifically closed.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|