DynamoDB: Query with KeyConditionExpression now throws exception on empty keys (#7065)
This commit is contained in:
parent
9e7295ddef
commit
20abb764a2
@ -19,6 +19,13 @@ class MockValidationException(DynamodbException):
|
|||||||
self.exception_msg = message
|
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):
|
class InvalidIndexNameError(MockValidationException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, List, Dict, Tuple, Optional, Union
|
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
|
from moto.utilities.tokenizer import GenericTokenizer
|
||||||
|
|
||||||
|
|
||||||
@ -209,6 +209,8 @@ def validate_schema(
|
|||||||
)
|
)
|
||||||
if comparison != "=":
|
if comparison != "=":
|
||||||
raise MockValidationException("Query key condition not supported")
|
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")
|
index_range_key = get_key(schema, "RANGE")
|
||||||
range_key, range_comparison, range_values = next(
|
range_key, range_comparison, range_values = next(
|
||||||
@ -219,9 +221,12 @@ def validate_schema(
|
|||||||
),
|
),
|
||||||
(None, None, []),
|
(None, None, []),
|
||||||
)
|
)
|
||||||
if index_range_key and len(results) > 1 and range_key != index_range_key:
|
if index_range_key:
|
||||||
raise MockValidationException(
|
if len(results) > 1 and range_key != index_range_key:
|
||||||
f"Query condition missed key schema element: {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]
|
return hash_value, range_comparison, range_values # type: ignore[return-value]
|
||||||
|
@ -14,6 +14,7 @@ from .exceptions import (
|
|||||||
MockValidationException,
|
MockValidationException,
|
||||||
ResourceNotFoundException,
|
ResourceNotFoundException,
|
||||||
UnknownKeyType,
|
UnknownKeyType,
|
||||||
|
KeyIsEmptyStringException,
|
||||||
)
|
)
|
||||||
from moto.dynamodb.models import dynamodb_backends, Table, DynamoDBBackend
|
from moto.dynamodb.models import dynamodb_backends, Table, DynamoDBBackend
|
||||||
from moto.dynamodb.models.utilities import dynamo_json_dump
|
from moto.dynamodb.models.utilities import dynamo_json_dump
|
||||||
@ -554,10 +555,7 @@ class DynamoHandler(BaseResponse):
|
|||||||
key = self.body["Key"]
|
key = self.body["Key"]
|
||||||
empty_keys = [k for k, v in key.items() if not next(iter(v.values()))]
|
empty_keys = [k for k, v in key.items() if not next(iter(v.values()))]
|
||||||
if empty_keys:
|
if empty_keys:
|
||||||
raise MockValidationException(
|
raise KeyIsEmptyStringException(empty_keys[0])
|
||||||
"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]}"
|
|
||||||
)
|
|
||||||
|
|
||||||
projection_expression = self._get_projection_expression()
|
projection_expression = self._get_projection_expression()
|
||||||
attributes_to_get = self.body.get("AttributesToGet")
|
attributes_to_get = self.body.get("AttributesToGet")
|
||||||
|
@ -3,6 +3,7 @@ import pytest
|
|||||||
|
|
||||||
from moto import mock_dynamodb
|
from moto import mock_dynamodb
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
|
from boto3.dynamodb.conditions import Key
|
||||||
from moto.dynamodb.limits import HASH_KEY_MAX_LENGTH, RANGE_KEY_MAX_LENGTH
|
from moto.dynamodb.limits import HASH_KEY_MAX_LENGTH, RANGE_KEY_MAX_LENGTH
|
||||||
|
|
||||||
|
|
||||||
@ -323,3 +324,40 @@ def test_item_add_empty_key_exception():
|
|||||||
ex.value.response["Error"]["Message"]
|
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"
|
== "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"
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user