Skip to content

Commit

Permalink
DynamoDB: Query with KeyConditionExpression now throws exception on e…
Browse files Browse the repository at this point in the history
…mpty keys (#7065)
  • Loading branch information
bblommers authored Nov 24, 2023
1 parent 9e7295d commit 20abb76
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 9 deletions.
7 changes: 7 additions & 0 deletions moto/dynamodb/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ def __init__(self, message: str):
self.exception_msg = message


class KeyIsEmptyStringException(MockValidationException):
def __init__(self, empty_key: str):
super().__init__(
message=f"One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: {empty_key}"
)


class InvalidIndexNameError(MockValidationException):
pass

Expand Down
15 changes: 10 additions & 5 deletions moto/dynamodb/parsing/key_condition_expression.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from enum import Enum
from typing import Any, List, Dict, Tuple, Optional, Union
from moto.dynamodb.exceptions import MockValidationException
from moto.dynamodb.exceptions import MockValidationException, KeyIsEmptyStringException
from moto.utilities.tokenizer import GenericTokenizer


Expand Down Expand Up @@ -209,6 +209,8 @@ def validate_schema(
)
if comparison != "=":
raise MockValidationException("Query key condition not supported")
if "S" in hash_value and hash_value["S"] == "":
raise KeyIsEmptyStringException(index_hash_key) # type: ignore[arg-type]

index_range_key = get_key(schema, "RANGE")
range_key, range_comparison, range_values = next(
Expand All @@ -219,9 +221,12 @@ def validate_schema(
),
(None, None, []),
)
if index_range_key and len(results) > 1 and range_key != index_range_key:
raise MockValidationException(
f"Query condition missed key schema element: {index_range_key}"
)
if index_range_key:
if len(results) > 1 and range_key != index_range_key:
raise MockValidationException(
f"Query condition missed key schema element: {index_range_key}"
)
if {"S": ""} in range_values:
raise KeyIsEmptyStringException(index_range_key)

return hash_value, range_comparison, range_values # type: ignore[return-value]
6 changes: 2 additions & 4 deletions moto/dynamodb/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
MockValidationException,
ResourceNotFoundException,
UnknownKeyType,
KeyIsEmptyStringException,
)
from moto.dynamodb.models import dynamodb_backends, Table, DynamoDBBackend
from moto.dynamodb.models.utilities import dynamo_json_dump
Expand Down Expand Up @@ -554,10 +555,7 @@ def get_item(self) -> str:
key = self.body["Key"]
empty_keys = [k for k, v in key.items() if not next(iter(v.values()))]
if empty_keys:
raise MockValidationException(
"One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an "
f"empty string value. Key: {empty_keys[0]}"
)
raise KeyIsEmptyStringException(empty_keys[0])

projection_expression = self._get_projection_expression()
attributes_to_get = self.body.get("AttributesToGet")
Expand Down
38 changes: 38 additions & 0 deletions tests/test_dynamodb/exceptions/test_key_length_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from moto import mock_dynamodb
from botocore.exceptions import ClientError
from boto3.dynamodb.conditions import Key
from moto.dynamodb.limits import HASH_KEY_MAX_LENGTH, RANGE_KEY_MAX_LENGTH


Expand Down Expand Up @@ -323,3 +324,40 @@ def test_item_add_empty_key_exception():
ex.value.response["Error"]["Message"]
== "One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: forum_name"
)


@mock_dynamodb
def test_query_empty_key_exception():
name = "TestTable"
conn = boto3.client("dynamodb", region_name="us-west-2")
conn.create_table(
TableName=name,
KeySchema=[
{"AttributeName": "hk", "KeyType": "HASH"},
{"AttributeName": "rk", "KeyType": "RANGE"},
],
AttributeDefinitions=[
{"AttributeName": "hk", "AttributeType": "S"},
{"AttributeName": "rk", "AttributeType": "S"},
],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
table = boto3.resource("dynamodb", "us-west-2").Table(name)

with pytest.raises(ClientError) as ex:
table.query(KeyConditionExpression=Key("hk").eq(""))
assert ex.value.response["Error"]["Code"] == "ValidationException"
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
assert (
ex.value.response["Error"]["Message"]
== "One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: hk"
)

with pytest.raises(ClientError) as ex:
table.query(KeyConditionExpression=Key("hk").eq("sth") & Key("rk").eq(""))
assert ex.value.response["Error"]["Code"] == "ValidationException"
assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
assert (
ex.value.response["Error"]["Message"]
== "One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: rk"
)

0 comments on commit 20abb76

Please sign in to comment.