DynamoDB - check nested attrs when validating duplicate set-actions (#5560)
This commit is contained in:
parent
67c688b187
commit
121e3eadb8
@ -27,9 +27,8 @@ class Node(metaclass=abc.ABCMeta):
|
||||
if nr_of_clauses > 1:
|
||||
raise TooManyAddClauses()
|
||||
set_actions = self.find_clauses(UpdateExpressionSetAction)
|
||||
set_attributes = [
|
||||
c.children[0].children[0].children[0] for c in set_actions
|
||||
]
|
||||
# set_attributes = ["attr", "map.attr", attr.list[2], ..]
|
||||
set_attributes = [s.children[0].to_str() for s in set_actions]
|
||||
# We currently only check for duplicates
|
||||
# We should also check for partial duplicates, i.e. [attr, attr.sub] is also invalid
|
||||
if len(set_attributes) != len(set(set_attributes)):
|
||||
@ -148,7 +147,8 @@ class UpdateExpressionDeleteAction(UpdateExpressionClause):
|
||||
|
||||
|
||||
class UpdateExpressionPath(UpdateExpressionClause):
|
||||
pass
|
||||
def to_str(self):
|
||||
return "".join(x.to_str() for x in self.children)
|
||||
|
||||
|
||||
class UpdateExpressionValue(UpdateExpressionClause):
|
||||
@ -186,6 +186,9 @@ class UpdateExpressionDeleteClause(UpdateExpressionClause):
|
||||
class ExpressionPathDescender(Node):
|
||||
"""Node identifying descender into nested structure (.) in expression"""
|
||||
|
||||
def to_str(self):
|
||||
return "."
|
||||
|
||||
|
||||
class ExpressionSelector(LeafNode):
|
||||
"""Node identifying selector [selection_index] in expresion"""
|
||||
@ -201,6 +204,9 @@ class ExpressionSelector(LeafNode):
|
||||
def get_index(self):
|
||||
return self.children[0]
|
||||
|
||||
def to_str(self):
|
||||
return f"[{self.get_index()}]"
|
||||
|
||||
|
||||
class ExpressionAttribute(LeafNode):
|
||||
"""An attribute identifier as used in the DDB item"""
|
||||
@ -211,6 +217,9 @@ class ExpressionAttribute(LeafNode):
|
||||
def get_attribute_name(self):
|
||||
return self.children[0]
|
||||
|
||||
def to_str(self):
|
||||
return self.get_attribute_name()
|
||||
|
||||
|
||||
class ExpressionAttributeName(LeafNode):
|
||||
"""An ExpressionAttributeName is an alias for an attribute identifier"""
|
||||
@ -221,6 +230,9 @@ class ExpressionAttributeName(LeafNode):
|
||||
def get_attribute_name_placeholder(self):
|
||||
return self.children[0]
|
||||
|
||||
def to_str(self):
|
||||
return self.get_attribute_name_placeholder()
|
||||
|
||||
|
||||
class ExpressionAttributeValue(LeafNode):
|
||||
"""An ExpressionAttributeValue is an alias for an value"""
|
||||
|
32
tests/test_dynamodb/test_dynamodb_update_expressions.py
Normal file
32
tests/test_dynamodb/test_dynamodb_update_expressions.py
Normal file
@ -0,0 +1,32 @@
|
||||
import boto3
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
from moto import mock_dynamodb
|
||||
|
||||
|
||||
@mock_dynamodb
|
||||
def test_update_different_map_elements_in_single_request():
|
||||
# https://github.com/spulec/moto/issues/5552
|
||||
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
|
||||
dynamodb.create_table(
|
||||
TableName="example_table",
|
||||
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
|
||||
AttributeDefinitions=[
|
||||
{"AttributeName": "id", "AttributeType": "S"},
|
||||
],
|
||||
BillingMode="PAY_PER_REQUEST",
|
||||
)
|
||||
record = {
|
||||
"id": "example_id",
|
||||
"d": {"hello": "h", "world": "w"},
|
||||
}
|
||||
table = dynamodb.Table("example_table")
|
||||
table.put_item(Item=record)
|
||||
updated = table.update_item(
|
||||
Key={"id": "example_id"},
|
||||
UpdateExpression="set d.hello = :h, d.world = :w",
|
||||
ExpressionAttributeValues={":h": "H", ":w": "W"},
|
||||
ReturnValues="ALL_NEW",
|
||||
)
|
||||
updated["Attributes"].should.equal(
|
||||
{"id": "example_id", "d": {"hello": "H", "world": "W"}}
|
||||
)
|
Loading…
Reference in New Issue
Block a user