diff --git a/moto/dynamodb2/parsing/executors.py b/moto/dynamodb2/parsing/executors.py index 2f2f2bb82..76642542d 100644 --- a/moto/dynamodb2/parsing/executors.py +++ b/moto/dynamodb2/parsing/executors.py @@ -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): diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index e2dd744e3..06dfec01e 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -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")