Repository: getmoto/moto

Issue:
Dynamodb query with a KeyConditionExpression should raise ClientError when the partition key is empty
Moto Version: 4.2.9

In an almost identical situation to https://github.com/getmoto/moto/issues/5345, which was about this issue when using `get_item`, when using `query` instead with a KeyConditionExpression the same case can occur.

```
from boto3 import resource
from boto3.dynamodb.conditions import Attr, ConditionBase, Key

table = resource("dynamodb").Table("my_table")
table.query(KeyConditionExpression=Key("PK").eq(""))
```

should cause `botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the Query operation: One or more parameter values are not valid. The AttributeValue for a key attribute cannot contain an empty string value. Key: PK`

Instead with moto it just returns the query as normal with no results.


Discussion:
Thanks for letting us know @LiamCato - I'll raise a PR for this shortly.

Patch:
diff --git a/moto/dynamodb/exceptions.py b/moto/dynamodb/exceptions.py
--- a/moto/dynamodb/exceptions.py
+++ b/moto/dynamodb/exceptions.py
@@ -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
 
diff --git a/moto/dynamodb/parsing/key_condition_expression.py b/moto/dynamodb/parsing/key_condition_expression.py
--- a/moto/dynamodb/parsing/key_condition_expression.py
+++ b/moto/dynamodb/parsing/key_condition_expression.py
@@ -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
 
 
@@ -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(
@@ -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]
diff --git a/moto/dynamodb/responses.py b/moto/dynamodb/responses.py
--- a/moto/dynamodb/responses.py
+++ b/moto/dynamodb/responses.py
@@ -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
@@ -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")


Test patch:
diff --git a/tests/test_dynamodb/exceptions/test_key_length_exceptions.py b/tests/test_dynamodb/exceptions/test_key_length_exceptions.py
--- a/tests/test_dynamodb/exceptions/test_key_length_exceptions.py
+++ b/tests/test_dynamodb/exceptions/test_key_length_exceptions.py
@@ -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
 
 
@@ -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"
+    )