Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions sdk/cosmos/azure-cosmos/azure/cosmos/aio/_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,10 +821,12 @@ def query_items(
feed_options["correlatedActivityId"] = GenerateGuidId()

# Set query with 'query' and 'parameters' from kwargs
if utils.valid_key_value_exist(kwargs, "parameters"):
query = {"query": kwargs.pop("query", None), "parameters": kwargs.pop("parameters", None)}
query_str = kwargs.pop("query", None)
parameters = kwargs.pop("parameters", None)
if parameters is not None:
query = {"query": query_str, "parameters": parameters}
else:
query = kwargs.pop("query", None)
query = query_str

# Set method to get/cache container properties
kwargs["containerProperties"] = self._get_properties_with_options
Expand Down
8 changes: 5 additions & 3 deletions sdk/cosmos/azure-cosmos/azure/cosmos/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -933,10 +933,12 @@ def query_items( # pylint:disable=docstring-missing-param
feed_options["containerRID"] = self.__get_client_container_caches()[self.container_link]["_rid"]

# Set query with 'query' and 'parameters' from kwargs
if utils.valid_key_value_exist(kwargs, "parameters"):
query = {"query": kwargs.pop("query", None), "parameters": kwargs.pop("parameters", None)}
query_str = kwargs.pop("query", None)
parameters = kwargs.pop("parameters", None)
if parameters is not None:
query = {"query": query_str, "parameters": parameters}
else:
query = kwargs.pop("query", None)
query = query_str

# Set range filters for a query. Options are either 'feed_range' or 'partition_key'
utils.verify_exclusive_arguments(["feed_range", "partition_key"], **kwargs)
Expand Down
99 changes: 99 additions & 0 deletions sdk/cosmos/azure-cosmos/tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,105 @@ def _MockNextFunction(self):
else:
raise StopIteration

def test_query_items_with_parameters_none(self):
"""Test that query_items handles parameters=None correctly (issue #43662)."""
created_collection = self.created_db.create_container(
"test_params_none_" + str(uuid.uuid4()), PartitionKey(path="/pk"))

# Create test documents
doc1_id = 'doc1_' + str(uuid.uuid4())
doc2_id = 'doc2_' + str(uuid.uuid4())
created_collection.create_item(body={'pk': 'pk1', 'id': doc1_id, 'value': 1})
created_collection.create_item(body={'pk': 'pk2', 'id': doc2_id, 'value': 2})

# Test 1: Explicitly passing parameters=None should not cause TypeError
query = 'SELECT * FROM c'
query_iterable = created_collection.query_items(
query=query,
parameters=None,
enable_cross_partition_query=True
)
results = list(query_iterable)
self.assertEqual(len(results), 2)

# Test 2: parameters=None with partition_key should work
query_iterable = created_collection.query_items(
query=query,
parameters=None,
partition_key='pk1'
)
results = list(query_iterable)
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['id'], doc1_id)

# Test 3: Verify parameterized query still works with actual parameters
query_with_params = 'SELECT * FROM c WHERE c.value = @value'
query_iterable = created_collection.query_items(
query=query_with_params,
parameters=[{'name': '@value', 'value': 2}],
enable_cross_partition_query=True
)
results = list(query_iterable)
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['id'], doc2_id)

# Test 4: Query without parameters argument should work (default behavior)
query_iterable = created_collection.query_items(
query=query,
enable_cross_partition_query=True
)
results = list(query_iterable)
self.assertEqual(len(results), 2)

self.created_db.delete_container(created_collection.id)

def test_query_items_parameters_none_with_options(self):
"""Test parameters=None works with various query options."""
created_collection = self.created_db.create_container(
"test_params_none_opts_" + str(uuid.uuid4()), PartitionKey(path="/pk"))

# Create multiple test documents
for i in range(5):
doc_id = f'doc_{i}_' + str(uuid.uuid4())
created_collection.create_item(body={'pk': 'test', 'id': doc_id, 'index': i})

# Test with parameters=None and max_item_count
query = 'SELECT * FROM c ORDER BY c.index'
query_iterable = created_collection.query_items(
query=query,
parameters=None,
partition_key='test',
max_item_count=2
)

# Verify pagination works
page_count = 0
total_items = 0
for page in query_iterable.by_page():
page_count += 1
items = list(page)
total_items += len(items)
self.assertLessEqual(len(items), 2)

self.assertEqual(total_items, 5)
self.assertGreaterEqual(page_count, 2) # Should have multiple pages

# Test with parameters=None and populate_query_metrics
query_iterable = created_collection.query_items(
query=query,
parameters=None,
partition_key='test',
populate_query_metrics=True
)
results = list(query_iterable)
self.assertEqual(len(results), 5)

# Verify query metrics were populated
metrics_header_name = 'x-ms-documentdb-query-metrics'
self.assertTrue(metrics_header_name in created_collection.client_connection.last_response_headers)

self.created_db.delete_container(created_collection.id)


if __name__ == "__main__":
unittest.main()
99 changes: 99 additions & 0 deletions sdk/cosmos/azure-cosmos/tests/test_query_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,105 @@ async def _MockExecuteFunctionTimeoutFailoverRetry(self, function, *args, **kwar
raise ex_to_raise
return await self.OriginalExecuteFunction(function, *args, **kwargs)

async def test_query_items_with_parameters_none_async(self):
"""Test that query_items handles parameters=None correctly (issue #43662)."""
created_collection = await self.created_db.create_container(
"test_params_none_" + str(uuid.uuid4()), PartitionKey(path="/pk"))

# Create test documents
doc1_id = 'doc1_' + str(uuid.uuid4())
doc2_id = 'doc2_' + str(uuid.uuid4())
await created_collection.create_item(body={'pk': 'pk1', 'id': doc1_id, 'value': 1})
await created_collection.create_item(body={'pk': 'pk2', 'id': doc2_id, 'value': 2})

# Test 1: Explicitly passing parameters=None should not cause TypeError
query = 'SELECT * FROM c'
query_iterable = created_collection.query_items(
query=query,
parameters=None,
enable_cross_partition_query=True
)
results = [item async for item in query_iterable]
assert len(results) == 2

# Test 2: parameters=None with partition_key should work
query_iterable = created_collection.query_items(
query=query,
parameters=None,
partition_key='pk1'
)
results = [item async for item in query_iterable]
assert len(results) == 1
assert results[0]['id'] == doc1_id

# Test 3: Verify parameterized query still works with actual parameters
query_with_params = 'SELECT * FROM c WHERE c.value = @value'
query_iterable = created_collection.query_items(
query=query_with_params,
parameters=[{'name': '@value', 'value': 2}],
enable_cross_partition_query=True
)
results = [item async for item in query_iterable]
assert len(results) == 1
assert results[0]['id'] == doc2_id

# Test 4: Query without parameters argument should work (default behavior)
query_iterable = created_collection.query_items(
query=query,
enable_cross_partition_query=True
)
results = [item async for item in query_iterable]
assert len(results) == 2

await self.created_db.delete_container(created_collection.id)

async def test_query_items_parameters_none_with_options_async(self):
"""Test parameters=None works with various query options."""
created_collection = await self.created_db.create_container(
"test_params_none_opts_" + str(uuid.uuid4()), PartitionKey(path="/pk"))

# Create multiple test documents
for i in range(5):
doc_id = f'doc_{i}_' + str(uuid.uuid4())
await created_collection.create_item(body={'pk': 'test', 'id': doc_id, 'index': i})

# Test with parameters=None and max_item_count
query = 'SELECT * FROM c ORDER BY c.index'
query_iterable = created_collection.query_items(
query=query,
parameters=None,
partition_key='test',
max_item_count=2
)

# Verify pagination works
page_count = 0
total_items = 0
async for page in query_iterable.by_page():
page_count += 1
items = [item async for item in page]
total_items += len(items)
assert len(items) <= 2

assert total_items == 5
assert page_count >= 2 # Should have multiple pages

# Test with parameters=None and populate_query_metrics
query_iterable = created_collection.query_items(
query=query,
parameters=None,
partition_key='test',
populate_query_metrics=True
)
results = [item async for item in query_iterable]
assert len(results) == 5

# Verify query metrics were populated
metrics_header_name = 'x-ms-documentdb-query-metrics'
assert metrics_header_name in created_collection.client_connection.last_response_headers

await self.created_db.delete_container(created_collection.id)


if __name__ == '__main__':
unittest.main()
Loading