Skip to content

Commit b5148ee

Browse files
committed
DDB-Enhanced: Fixed NPE in DeleteItemOperation when ConditionExpression has null attributeNames or attributeValues
1 parent 535c24f commit b5148ee

File tree

5 files changed

+80
-4
lines changed

5 files changed

+80
-4
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS DynamoDB Enhanced Client",
4+
"description": "Fixed a bug causing a NullPointerException to be thrown in the enhanced DeleteItem operation if a conditionExpression was given with null attributeNames or null attributeValues."
5+
}

services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/DeleteItemOperation.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.enhanced.dynamodb.internal.operations;
1717

18+
import java.util.Map;
1819
import java.util.concurrent.CompletableFuture;
1920
import java.util.function.Function;
2021
import software.amazon.awssdk.annotations.SdkInternalApi;
@@ -26,6 +27,7 @@
2627
import software.amazon.awssdk.enhanced.dynamodb.model.DeleteItemEnhancedRequest;
2728
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
2829
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
30+
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
2931
import software.amazon.awssdk.services.dynamodb.model.Delete;
3032
import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest;
3133
import software.amazon.awssdk.services.dynamodb.model.DeleteItemResponse;
@@ -123,14 +125,16 @@ public TransactWriteItem generateTransactWriteItem(TableSchema<T> tableSchema,
123125
private DeleteItemRequest.Builder addExpressionsIfExist(DeleteItemRequest.Builder requestBuilder) {
124126
if (this.request.conditionExpression() != null) {
125127
requestBuilder = requestBuilder.conditionExpression(this.request.conditionExpression().expression());
128+
Map<String, String> expressionNames = this.request.conditionExpression().expressionNames();
129+
Map<String, AttributeValue> expressionValues = this.request.conditionExpression().expressionValues();
126130

127131
// Avoiding adding empty collections that the low level SDK will propagate to DynamoDb where it causes error.
128-
if (!this.request.conditionExpression().expressionNames().isEmpty()) {
129-
requestBuilder = requestBuilder.expressionAttributeNames(this.request.conditionExpression().expressionNames());
132+
if (expressionNames != null && !expressionNames.isEmpty()) {
133+
requestBuilder = requestBuilder.expressionAttributeNames(expressionNames);
130134
}
131135

132-
if (!this.request.conditionExpression().expressionValues().isEmpty()) {
133-
requestBuilder = requestBuilder.expressionAttributeValues(this.request.conditionExpression().expressionValues());
136+
if (expressionValues != null && !expressionValues.isEmpty()) {
137+
requestBuilder = requestBuilder.expressionAttributeValues(expressionValues);
134138
}
135139
}
136140
return requestBuilder;

services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/DeleteItemOperationTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.enhanced.dynamodb.internal.operations;
1717

18+
import static java.util.Collections.emptyMap;
1819
import static java.util.Collections.singletonList;
1920
import static org.hamcrest.MatcherAssert.assertThat;
2021
import static org.hamcrest.Matchers.is;
@@ -65,6 +66,7 @@ public class DeleteItemOperationTest {
6566
private static final OperationContext GSI_1_CONTEXT =
6667
DefaultOperationContext.create(TABLE_NAME, "gsi_1");
6768
private static final Expression CONDITION_EXPRESSION;
69+
private static final Expression MINIMAL_CONDITION_EXPRESSION = Expression.builder().expression("foo = bar").build();
6870

6971
static {
7072
Map<String, String> expressionNames = new HashMap<>();
@@ -162,6 +164,24 @@ public void generateRequest_withConditionExpression() {
162164
assertThat(request.expressionAttributeValues(), is(CONDITION_EXPRESSION.expressionValues()));
163165
}
164166

167+
@Test
168+
public void generateRequest_withMinimalConditionExpression() {
169+
FakeItem keyItem = createUniqueFakeItem();
170+
DeleteItemOperation<FakeItem> deleteItemOperation =
171+
DeleteItemOperation.create(DeleteItemEnhancedRequest.builder()
172+
.key(k -> k.partitionValue(keyItem.getId()))
173+
.conditionExpression(MINIMAL_CONDITION_EXPRESSION)
174+
.build());
175+
176+
DeleteItemRequest request = deleteItemOperation.generateRequest(FakeItem.getTableSchema(),
177+
PRIMARY_CONTEXT,
178+
null);
179+
180+
assertThat(request.conditionExpression(), is(MINIMAL_CONDITION_EXPRESSION.expression()));
181+
assertThat(request.expressionAttributeNames(), is(emptyMap()));
182+
assertThat(request.expressionAttributeValues(), is(emptyMap()));
183+
}
184+
165185
@Test(expected = IllegalArgumentException.class)
166186
public void generateRequest_noPartitionKey_throwsIllegalArgumentException() {
167187
DeleteItemOperation<FakeItemComposedClass> deleteItemOperation =

services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/PutItemOperationTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.enhanced.dynamodb.internal.operations;
1717

18+
import static java.util.Collections.emptyMap;
1819
import static org.hamcrest.MatcherAssert.assertThat;
1920
import static org.hamcrest.Matchers.is;
2021
import static org.hamcrest.Matchers.nullValue;
@@ -61,6 +62,7 @@ public class PutItemOperationTest {
6162
DefaultOperationContext.create(TABLE_NAME, "gsi_1");
6263
private static final Expression CONDITION_EXPRESSION;
6364
private static final Expression CONDITION_EXPRESSION_2;
65+
private static final Expression MINIMAL_CONDITION_EXPRESSION = Expression.builder().expression("foo = bar").build();
6466

6567
static {
6668
Map<String, String> expressionNames = new HashMap<>();
@@ -166,6 +168,24 @@ public void generateRequest_withConditionExpression_generatesCorrectRequest() {
166168
assertThat(request, is(expectedRequest));
167169
}
168170

171+
@Test
172+
public void generateRequest_withMinimalConditionExpression() {
173+
FakeItem fakeItem = createUniqueFakeItem();
174+
PutItemOperation<FakeItem> putItemOperation =
175+
PutItemOperation.create(PutItemEnhancedRequest.builder(FakeItem.class)
176+
.item(fakeItem)
177+
.conditionExpression(MINIMAL_CONDITION_EXPRESSION)
178+
.build());
179+
180+
PutItemRequest request = putItemOperation.generateRequest(FakeItem.getTableSchema(),
181+
PRIMARY_CONTEXT,
182+
null);
183+
184+
assertThat(request.conditionExpression(), is(MINIMAL_CONDITION_EXPRESSION.expression()));
185+
assertThat(request.expressionAttributeNames(), is(emptyMap()));
186+
assertThat(request.expressionAttributeValues(), is(emptyMap()));
187+
}
188+
169189
@Test
170190
public void generateRequest_withConditionExpression_andExtensionWithSingleCondition() {
171191
FakeItem baseFakeItem = createUniqueFakeItem();

services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/UpdateItemOperationTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ public class UpdateItemOperationTest {
7676
private static final OperationContext GSI_1_CONTEXT =
7777
DefaultOperationContext.create(TABLE_NAME, "gsi_1");
7878
private static final Expression CONDITION_EXPRESSION;
79+
private static final Expression MINIMAL_CONDITION_EXPRESSION = Expression.builder().expression("foo = bar").build();
80+
7981

8082
static {
8183
Map<String, String> expressionNames = new HashMap<>();
@@ -190,6 +192,31 @@ public void generateRequest_withConditionExpression() {
190192
assertThat(request, is(expectedRequest));
191193
}
192194

195+
@Test
196+
public void generateRequest_withMinimalConditionExpression() {
197+
FakeItemWithSort item = createUniqueFakeItemWithSort();
198+
item.setOtherAttribute1("value-1");
199+
200+
UpdateItemOperation<FakeItemWithSort> updateItemOperation =
201+
UpdateItemOperation.create(UpdateItemEnhancedRequest.builder(FakeItemWithSort.class)
202+
.item(item)
203+
.conditionExpression(MINIMAL_CONDITION_EXPRESSION)
204+
.build());
205+
206+
UpdateItemRequest request = updateItemOperation.generateRequest(FakeItemWithSort.getTableSchema(),
207+
PRIMARY_CONTEXT,
208+
null);
209+
210+
Map<String, AttributeValue> expectedValues = new HashMap<>();
211+
expectedValues.put(OTHER_ATTRIBUTE_1_VALUE, AttributeValue.builder().s("value-1").build());
212+
Map<String, String> expectedNames = new HashMap<>();
213+
expectedNames.put(OTHER_ATTRIBUTE_1_NAME, "other_attribute_1");
214+
expectedNames.put(OTHER_ATTRIBUTE_2_NAME, "other_attribute_2");
215+
assertThat(request.conditionExpression(), is(MINIMAL_CONDITION_EXPRESSION.expression()));
216+
assertThat(request.expressionAttributeNames(), is(expectedNames));
217+
assertThat(request.expressionAttributeValues(), is(expectedValues));
218+
}
219+
193220
@Test
194221
public void generateRequest_explicitlyUnsetIgnoreNulls() {
195222
FakeItemWithSort item = createUniqueFakeItemWithSort();

0 commit comments

Comments
 (0)