Skip to content

Commit 1a0cde3

Browse files
committed
DynamoDB: BETWEEN no longer excludes an attribute value of 0
FuncBetween.expr() tested the attribute with bare truthiness, so a Decimal('0') value failed the condition and was excluded even when it fell inside the range. Apply the same zero-safe check start/end already use. Regression test fails on main; condition-expression suite passes.
1 parent 7ffce30 commit 1a0cde3

2 files changed

Lines changed: 37 additions & 2 deletions

File tree

moto/dynamodb/comparisons.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,12 +1233,14 @@ def expr(self, item: Item | None) -> bool:
12331233
# Can't just check 'if start', because start could be 0, which is a valid number
12341234
start_has_value = start is not None and (isinstance(start, Decimal) or start)
12351235
end_has_value = end is not None and (isinstance(end, Decimal) or end)
1236-
if start_has_value and attr and end_has_value:
1236+
# The tested attribute needs the same zero-safe check, otherwise a value of 0 is excluded
1237+
attr_has_value = attr is not None and (isinstance(attr, Decimal) or attr)
1238+
if start_has_value and attr_has_value and end_has_value:
12371239
return start <= attr <= end
12381240
elif start is None and attr is None:
12391241
# None is between None and None as well as None is between None and any number
12401242
return True
1241-
elif start is None and attr and end:
1243+
elif start is None and attr_has_value and end_has_value:
12421244
return attr <= end
12431245
else:
12441246
return False

tests/test_dynamodb/test_dynamodb_condition_expressions.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,3 +739,36 @@ def test_conditional_check_failed_bytes():
739739
assert "Item" in exc.value.response
740740
assert exc.value.response["Item"]["my_bytes"]["B"] == b"somebytes"
741741
assert exc.value.response["Item"]["my_bytes_set"]["BS"] == [b"byte1", b"byte2"]
742+
743+
744+
@mock_aws
745+
def test_between_condition_includes_zero():
746+
"""
747+
A numeric attribute value of exactly 0 must satisfy a BETWEEN range that
748+
includes it; previously FuncBetween tested the attribute with bare
749+
truthiness, so Decimal("0") was treated as missing and excluded.
750+
"""
751+
dynamodb = boto3.resource("dynamodb", region_name="us-east-2")
752+
table_name = f"T{uuid4()}"
753+
dynamodb.create_table(
754+
TableName=table_name,
755+
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
756+
AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
757+
BillingMode="PAY_PER_REQUEST",
758+
)
759+
table = dynamodb.Table(table_name)
760+
761+
table.put_item(Item={"id": "zero", "price": 0})
762+
table.put_item(Item={"id": "five", "price": 5})
763+
764+
result = table.scan(
765+
FilterExpression="price BETWEEN :lo AND :hi",
766+
ExpressionAttributeValues={":lo": 0, ":hi": 100},
767+
)
768+
assert {item["id"] for item in result["Items"]} == {"zero", "five"}
769+
770+
result = table.scan(
771+
FilterExpression="price BETWEEN :lo AND :hi",
772+
ExpressionAttributeValues={":lo": -10, ":hi": 0},
773+
)
774+
assert {item["id"] for item in result["Items"]} == {"zero"}

0 commit comments

Comments
 (0)