DynamoDB - Validate the nr of Add-clauses on update (#4398)

This commit is contained in:
Bert Blommers 2021-10-12 19:32:10 +00:00 committed by GitHub
parent 52df393b5a
commit 58df83f39f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 46 additions and 0 deletions

View File

@ -204,3 +204,10 @@ class UpdateHashRangeKeyException(MockValidationException):
def __init__(self, key_name):
super(UpdateHashRangeKeyException, self).__init__(self.msg.format(key_name))
class TooManyAddClauses(InvalidUpdateExpression):
msg = 'The "ADD" section can only be used once in an update expression;'
def __init__(self):
super(TooManyAddClauses, self).__init__(self.msg)

View File

@ -1388,6 +1388,7 @@ class DynamoDBBackend(BaseBackend):
# Parse expression to get validation errors
update_expression_ast = UpdateExpressionParser.make(update_expression)
update_expression = re.sub(r"\s*([=\+-])\s*", "\\1", update_expression)
update_expression_ast.validate()
if all([table.hash_key_attr in key, table.range_key_attr in key]):
# Covers cases where table has hash and range keys, ``key`` param

View File

@ -3,6 +3,7 @@ from abc import abstractmethod
from collections import deque
from moto.dynamodb2.models import DynamoType
from ..exceptions import TooManyAddClauses
class Node(metaclass=abc.ABCMeta):
@ -20,6 +21,21 @@ class Node(metaclass=abc.ABCMeta):
def set_parent(self, parent_node):
self.parent = parent_node
def validate(self):
if self.type == "UpdateExpression":
nr_of_clauses = len(self.find_clauses(UpdateExpressionAddClause))
if nr_of_clauses > 1:
raise TooManyAddClauses()
def find_clauses(self, clause_type):
clauses = []
for child in self.children or []:
if isinstance(child, clause_type):
clauses.append(child)
elif isinstance(child, Expression):
clauses.extend(child.find_clauses(clause_type))
return clauses
class LeafNode(Node):
"""A LeafNode is a Node where none of the children are Nodes themselves."""

View File

@ -147,3 +147,25 @@ def test_empty_expressionattributenames_with_projection():
err = exc.value.response["Error"]
err["Code"].should.equal("ValidationException")
err["Message"].should.equal("ExpressionAttributeNames must not be empty")
@mock_dynamodb2
def test_update_item_range_key_set():
ddb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table.
table = ddb.create_table(
TableName="test-table", BillingMode="PAY_PER_REQUEST", **table_schema
)
with pytest.raises(ClientError) as exc:
table.update_item(
Key={"partitionKey": "the-key"},
UpdateExpression="ADD x :one SET a = :a ADD y :one",
ExpressionAttributeValues={":one": 1, ":a": "lore ipsum"},
)
err = exc.value.response["Error"]
err["Code"].should.equal("ValidationException")
err["Message"].should.equal(
'Invalid UpdateExpression: The "ADD" section can only be used once in an update expression;'
)