Fix: Empty sets not removed from item after UpdateExpression (#3386)

DynamoDB does not support empty sets. If the last item in a set is
deleted as part of an UpdateExpression, the attribute must be removed.

Ref: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html

Fixes #3296
This commit is contained in:
Brian Pandola 2020-10-14 08:32:42 -07:00 committed by GitHub
parent ccda76898a
commit 99556620a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 0 deletions

View File

@ -161,6 +161,13 @@ class DeleteExecutor(NodeExecutor):
# DynamoDB does not mind if value is not present
pass
# DynamoDB does not support empty sets. If we've deleted
# the last item in the set, we have to remove the attribute.
if not string_set_list:
element = self.get_element_to_action()
container = self.get_item_before_end_of_path(item)
container.pop(element.get_attribute_name())
class RemoveExecutor(NodeExecutor):
def execute(self, item):

View File

@ -5445,3 +5445,37 @@ def test_lsi_projection_type_keys_only():
items[0].should.equal(
{"partitionKey": "pk-1", "sortKey": "sk-1", "lsiK1SortKey": "lsi-sk"}
)
@mock_dynamodb2
def test_set_attribute_is_dropped_if_empty_after_update_expression():
table_name, item_key, set_item = "test-table", "test-id", "test-data"
client = boto3.client("dynamodb", region_name="us-east-1")
client.create_table(
TableName=table_name,
KeySchema=[{"AttributeName": "customer", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "customer", "AttributeType": "S"}],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
client.update_item(
TableName=table_name,
Key={"customer": {"S": item_key}},
UpdateExpression="ADD orders :order",
ExpressionAttributeValues={":order": {"SS": [set_item]}},
)
resp = client.scan(TableName=table_name, ProjectionExpression="customer, orders")
item = resp["Items"][0]
item.should.have.key("customer")
item.should.have.key("orders")
client.update_item(
TableName=table_name,
Key={"customer": {"S": item_key}},
UpdateExpression="DELETE orders :order",
ExpressionAttributeValues={":order": {"SS": [set_item]}},
)
resp = client.scan(TableName=table_name, ProjectionExpression="customer, orders")
item = resp["Items"][0]
item.should.have.key("customer")
item.should_not.have.key("orders")