Validate AST before creating new item (#3754)

This commit is contained in:
Bert Blommers 2021-08-28 09:38:12 +01:00 committed by GitHub
parent 34c00e7dbd
commit 4653c34fd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 0 deletions

View File

@ -1407,6 +1407,22 @@ class DynamoDBBackend(BaseBackend):
# Update does not fail on new items, so create one
if item is None:
if update_expression:
# Validate AST before creating anything
item = Item(
hash_value,
table.hash_key_type,
range_value,
table.range_key_type,
attrs={},
)
UpdateExpressionValidator(
update_expression_ast,
expression_attribute_names=expression_attribute_names,
expression_attribute_values=expression_attribute_values,
item=item,
table=table,
).validate()
data = {table.hash_key_attr: {hash_value.type: hash_value.value}}
if range_value:
data.update(

View File

@ -6245,3 +6245,35 @@ def test_describe_endpoints(region):
},
]
)
@mock_dynamodb2
def test_update_non_existing_item_raises_error_and_does_not_contain_item_afterwards():
"""
https://github.com/spulec/moto/issues/3729
Exception is raised, but item was persisted anyway
Happened because we would create a placeholder, before validating/executing the UpdateExpression
:return:
"""
name = "TestTable"
conn = boto3.client("dynamodb", region_name="us-west-2")
hkey = "primary_partition_key"
conn.create_table(
TableName=name,
KeySchema=[{"AttributeName": hkey, "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": hkey, "AttributeType": "S"}],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
update_expression = {
"Key": {hkey: "some_identification_string"},
"UpdateExpression": "set #AA.#AB = :aa",
"ExpressionAttributeValues": {":aa": "abc"},
"ExpressionAttributeNames": {"#AA": "some_dict", "#AB": "key1"},
"ConditionExpression": "attribute_not_exists(#AA.#AB)",
}
table = boto3.resource("dynamodb", region_name="us-west-2").Table(name)
with pytest.raises(ClientError) as err:
table.update_item(**update_expression)
err.value.response["Error"]["Code"].should.equal("ValidationException")
conn.scan(TableName=name)["Items"].should.have.length_of(0)