DynamoDB - do not allow HashKey to be changed on update (#5620)
This commit is contained in:
parent
cf5e7b750f
commit
0f9a907af0
@ -341,8 +341,9 @@ class EmptyStringKeyValueValidator(DepthFirstTraverser):
|
|||||||
|
|
||||||
|
|
||||||
class UpdateHashRangeKeyValidator(DepthFirstTraverser):
|
class UpdateHashRangeKeyValidator(DepthFirstTraverser):
|
||||||
def __init__(self, table_key_attributes):
|
def __init__(self, table_key_attributes, expression_attribute_names):
|
||||||
self.table_key_attributes = table_key_attributes
|
self.table_key_attributes = table_key_attributes
|
||||||
|
self.expression_attribute_names = expression_attribute_names
|
||||||
|
|
||||||
def _processing_map(self):
|
def _processing_map(self):
|
||||||
return {UpdateExpressionPath: self.check_for_hash_or_range_key}
|
return {UpdateExpressionPath: self.check_for_hash_or_range_key}
|
||||||
@ -350,6 +351,9 @@ class UpdateHashRangeKeyValidator(DepthFirstTraverser):
|
|||||||
def check_for_hash_or_range_key(self, node):
|
def check_for_hash_or_range_key(self, node):
|
||||||
"""Check that hash and range keys are not updated"""
|
"""Check that hash and range keys are not updated"""
|
||||||
key_to_update = node.children[0].children[0]
|
key_to_update = node.children[0].children[0]
|
||||||
|
key_to_update = self.expression_attribute_names.get(
|
||||||
|
key_to_update, key_to_update
|
||||||
|
)
|
||||||
if key_to_update in self.table_key_attributes:
|
if key_to_update in self.table_key_attributes:
|
||||||
raise UpdateHashRangeKeyException(key_to_update)
|
raise UpdateHashRangeKeyException(key_to_update)
|
||||||
return node
|
return node
|
||||||
@ -400,7 +404,9 @@ class UpdateExpressionValidator(Validator):
|
|||||||
def get_ast_processors(self):
|
def get_ast_processors(self):
|
||||||
"""Get the different processors that go through the AST tree and processes the nodes."""
|
"""Get the different processors that go through the AST tree and processes the nodes."""
|
||||||
processors = [
|
processors = [
|
||||||
UpdateHashRangeKeyValidator(self.table.table_key_attrs),
|
UpdateHashRangeKeyValidator(
|
||||||
|
self.table.table_key_attrs, self.expression_attribute_names or {}
|
||||||
|
),
|
||||||
ExpressionAttributeValueProcessor(self.expression_attribute_values),
|
ExpressionAttributeValueProcessor(self.expression_attribute_values),
|
||||||
ExpressionAttributeResolvingProcessor(
|
ExpressionAttributeResolvingProcessor(
|
||||||
self.expression_attribute_names, self.item
|
self.expression_attribute_names, self.item
|
||||||
|
@ -798,3 +798,75 @@ def test_transact_write_items_multiple_operations_fail():
|
|||||||
err["Message"]
|
err["Message"]
|
||||||
== "TransactItems can only contain one of Check, Put, Update or Delete"
|
== "TransactItems can only contain one of Check, Put, Update or Delete"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_dynamodb
|
||||||
|
def test_update_primary_key_with_sortkey():
|
||||||
|
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
|
||||||
|
schema = {
|
||||||
|
"KeySchema": [
|
||||||
|
{"AttributeName": "pk", "KeyType": "HASH"},
|
||||||
|
{"AttributeName": "sk", "KeyType": "RANGE"},
|
||||||
|
],
|
||||||
|
"AttributeDefinitions": [
|
||||||
|
{"AttributeName": "pk", "AttributeType": "S"},
|
||||||
|
{"AttributeName": "sk", "AttributeType": "S"},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
dynamodb.create_table(
|
||||||
|
TableName="test-table", BillingMode="PAY_PER_REQUEST", **schema
|
||||||
|
)
|
||||||
|
|
||||||
|
table = dynamodb.Table("test-table")
|
||||||
|
base_item = {"pk": "testchangepk", "sk": "else"}
|
||||||
|
table.put_item(Item=base_item)
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
table.update_item(
|
||||||
|
Key={"pk": "n/a", "sk": "else"},
|
||||||
|
UpdateExpression="SET #attr1 = :val1",
|
||||||
|
ExpressionAttributeNames={"#attr1": "pk"},
|
||||||
|
ExpressionAttributeValues={":val1": "different"},
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("ValidationException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"One or more parameter values were invalid: Cannot update attribute pk. This attribute is part of the key"
|
||||||
|
)
|
||||||
|
|
||||||
|
table.get_item(Key={"pk": "testchangepk", "sk": "else"})["Item"].should.equal(
|
||||||
|
{"pk": "testchangepk", "sk": "else"}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_dynamodb
|
||||||
|
def test_update_primary_key():
|
||||||
|
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
|
||||||
|
schema = {
|
||||||
|
"KeySchema": [{"AttributeName": "pk", "KeyType": "HASH"}],
|
||||||
|
"AttributeDefinitions": [{"AttributeName": "pk", "AttributeType": "S"}],
|
||||||
|
}
|
||||||
|
dynamodb.create_table(
|
||||||
|
TableName="without_sk", BillingMode="PAY_PER_REQUEST", **schema
|
||||||
|
)
|
||||||
|
|
||||||
|
table = dynamodb.Table("without_sk")
|
||||||
|
base_item = {"pk": "testchangepk"}
|
||||||
|
table.put_item(Item=base_item)
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
table.update_item(
|
||||||
|
Key={"pk": "n/a"},
|
||||||
|
UpdateExpression="SET #attr1 = :val1",
|
||||||
|
ExpressionAttributeNames={"#attr1": "pk"},
|
||||||
|
ExpressionAttributeValues={":val1": "different"},
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("ValidationException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"One or more parameter values were invalid: Cannot update attribute pk. This attribute is part of the key"
|
||||||
|
)
|
||||||
|
|
||||||
|
table.get_item(Key={"pk": "testchangepk"})["Item"].should.equal(
|
||||||
|
{"pk": "testchangepk"}
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user