diff --git a/moto/dynamodb2/parsing/executors.py b/moto/dynamodb2/parsing/executors.py index 76642542..538e26cb 100644 --- a/moto/dynamodb2/parsing/executors.py +++ b/moto/dynamodb2/parsing/executors.py @@ -165,8 +165,18 @@ class DeleteExecutor(NodeExecutor): # the last item in the set, we have to remove the attribute. if not string_set_list: element = self.get_element_to_action() + if isinstance(element, ExpressionAttributeName): + attribute_name = self.expression_attribute_names[ + element.get_attribute_name_placeholder() + ] + elif isinstance(element, ExpressionAttribute): + attribute_name = element.get_attribute_name() + else: + raise NotImplementedError( + "Moto does not support deleting {t} yet".format(t=type(element)) + ) container = self.get_item_before_end_of_path(item) - container.pop(element.get_attribute_name()) + del container[attribute_name] class RemoveExecutor(NodeExecutor): diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index 446d0238..352f542b 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -5598,8 +5598,14 @@ def test_lsi_projection_type_keys_only(): @mock_dynamodb2 -def test_set_attribute_is_dropped_if_empty_after_update_expression(): +@pytest.mark.parametrize( + "attr_name", + ["orders", "#placeholder"], + ids=["use attribute name", "use expression attribute name"], +) +def test_set_attribute_is_dropped_if_empty_after_update_expression(attr_name): table_name, item_key, set_item = "test-table", "test-id", "test-data" + expression_attribute_names = {"#placeholder": "orders"} client = boto3.client("dynamodb", region_name="us-east-1") client.create_table( TableName=table_name, @@ -5611,7 +5617,8 @@ def test_set_attribute_is_dropped_if_empty_after_update_expression(): client.update_item( TableName=table_name, Key={"customer": {"S": item_key}}, - UpdateExpression="ADD orders :order", + UpdateExpression="ADD {} :order".format(attr_name), + ExpressionAttributeNames=expression_attribute_names, ExpressionAttributeValues={":order": {"SS": [set_item]}}, ) resp = client.scan(TableName=table_name, ProjectionExpression="customer, orders") @@ -5622,7 +5629,8 @@ def test_set_attribute_is_dropped_if_empty_after_update_expression(): client.update_item( TableName=table_name, Key={"customer": {"S": item_key}}, - UpdateExpression="DELETE orders :order", + UpdateExpression="DELETE {} :order".format(attr_name), + ExpressionAttributeNames=expression_attribute_names, ExpressionAttributeValues={":order": {"SS": [set_item]}}, ) resp = client.scan(TableName=table_name, ProjectionExpression="customer, orders") diff --git a/tests/test_dynamodb2/test_dynamodb_executor.py b/tests/test_dynamodb2/test_dynamodb_executor.py index 577a5bae..57cd9b5c 100644 --- a/tests/test_dynamodb2/test_dynamodb_executor.py +++ b/tests/test_dynamodb2/test_dynamodb_executor.py @@ -282,24 +282,26 @@ def test_execution_of_remove_in_list(table): assert expected_item == item -def test_execution_of_delete_element_from_set(table): - update_expression = "delete s :value" +@pytest.mark.parametrize("attr_name", ["s", "#placeholder"]) +def test_execution_of_delete_element_from_set(table, attr_name): + expression_attribute_names = {"#placeholder": "s"} + update_expression = "delete {} :value".format(attr_name) update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, - attrs={"id": {"S": "foo2"}, "s": {"SS": ["value1", "value2", "value3"]},}, + attrs={"id": {"S": "foo2"}, "s": {"SS": ["value1", "value2", "value3"]}}, ) validated_ast = UpdateExpressionValidator( update_expression_ast, - expression_attribute_names=None, + expression_attribute_names=expression_attribute_names, expression_attribute_values={":value": {"SS": ["value2", "value5"]}}, item=item, table=table, ).validate() - UpdateExpressionExecutor(validated_ast, item, None).execute() + UpdateExpressionExecutor(validated_ast, item, expression_attribute_names).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", @@ -309,6 +311,26 @@ def test_execution_of_delete_element_from_set(table): ) assert expected_item == item + # delete last elements + update_expression = "delete {} :value".format(attr_name) + update_expression_ast = UpdateExpressionParser.make(update_expression) + validated_ast = UpdateExpressionValidator( + update_expression_ast, + expression_attribute_names=expression_attribute_names, + expression_attribute_values={":value": {"SS": ["value1", "value3"]}}, + item=item, + table=table, + ).validate() + UpdateExpressionExecutor(validated_ast, item, expression_attribute_names).execute() + expected_item = Item( + hash_key=DynamoType({"S": "id"}), + hash_key_type="TYPE", + range_key=None, + range_key_type=None, + attrs={"id": {"S": "foo2"}}, + ) + assert expected_item == item + def test_execution_of_add_number(table): update_expression = "add s :value"