DynamoDB: Improve support for Update-statements in batch_execute-statement() (#7297)

This commit is contained in:
Bert Blommers 2024-02-02 19:29:48 +00:00 committed by GitHub
parent 3e775cad57
commit 62647ab1a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 97 additions and 24 deletions

View File

@ -855,27 +855,31 @@ class DynamoDBBackend(BaseBackend):
} }
else: else:
response["TableName"] = table_name response["TableName"] = table_name
table = self.tables[table_name] if metadata.is_select_query():
for required_attr in table.table_key_attrs: table = self.tables[table_name]
if required_attr not in filter_keys: for required_attr in table.table_key_attrs:
response["Error"] = { if required_attr not in filter_keys:
"Code": "ValidationError", response["Error"] = {
"Message": "Select statements within BatchExecuteStatement must specify the primary key in the where clause.", "Code": "ValidationError",
} "Message": "Select statements within BatchExecuteStatement must specify the primary key in the where clause.",
}
responses.append(response) responses.append(response)
# Execution # Execution
for idx, stmt in enumerate(statements): for idx, stmt in enumerate(statements):
if "Error" in responses[idx]: if "Error" in responses[idx]:
continue continue
items = self.execute_statement( try:
statement=stmt["Statement"], parameters=stmt.get("Parameters", []) items = self.execute_statement(
) statement=stmt["Statement"], parameters=stmt.get("Parameters", [])
# Statements should always contain a HashKey and SortKey )
# An item with those keys may not exist # Statements should always contain a HashKey and SortKey
if items: # An item with those keys may not exist
# But if it does, it will always only contain one item at most if items:
responses[idx]["Item"] = items[0] # But if it does, it will always only contain one item at most
responses[idx]["Item"] = items[0]
except Exception as e:
responses[idx] = {"Error": {"Code": e.name, "Message": e.message}} # type: ignore
return responses return responses

View File

@ -54,7 +54,7 @@ all =
openapi-spec-validator>=0.5.0 openapi-spec-validator>=0.5.0
pyparsing>=3.0.7 pyparsing>=3.0.7
jsondiff>=1.1.2 jsondiff>=1.1.2
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
aws-xray-sdk!=0.96,>=0.93 aws-xray-sdk!=0.96,>=0.93
setuptools setuptools
multipart multipart
@ -69,7 +69,7 @@ proxy =
openapi-spec-validator>=0.5.0 openapi-spec-validator>=0.5.0
pyparsing>=3.0.7 pyparsing>=3.0.7
jsondiff>=1.1.2 jsondiff>=1.1.2
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
aws-xray-sdk!=0.96,>=0.93 aws-xray-sdk!=0.96,>=0.93
setuptools setuptools
multipart multipart
@ -84,7 +84,7 @@ server =
openapi-spec-validator>=0.5.0 openapi-spec-validator>=0.5.0
pyparsing>=3.0.7 pyparsing>=3.0.7
jsondiff>=1.1.2 jsondiff>=1.1.2
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
aws-xray-sdk!=0.96,>=0.93 aws-xray-sdk!=0.96,>=0.93
setuptools setuptools
flask!=2.2.0,!=2.2.1 flask!=2.2.0,!=2.2.1
@ -122,7 +122,7 @@ cloudformation =
openapi-spec-validator>=0.5.0 openapi-spec-validator>=0.5.0
pyparsing>=3.0.7 pyparsing>=3.0.7
jsondiff>=1.1.2 jsondiff>=1.1.2
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
aws-xray-sdk!=0.96,>=0.93 aws-xray-sdk!=0.96,>=0.93
setuptools setuptools
cloudfront = cloudfront =
@ -145,10 +145,10 @@ dms =
ds = ds =
dynamodb = dynamodb =
docker>=3.0.0 docker>=3.0.0
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
dynamodbstreams = dynamodbstreams =
docker>=3.0.0 docker>=3.0.0
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
ebs = ebs =
ec2 = sshpubkeys>=3.1.0 ec2 = sshpubkeys>=3.1.0
ec2instanceconnect = ec2instanceconnect =
@ -213,15 +213,15 @@ resourcegroupstaggingapi =
openapi-spec-validator>=0.5.0 openapi-spec-validator>=0.5.0
pyparsing>=3.0.7 pyparsing>=3.0.7
jsondiff>=1.1.2 jsondiff>=1.1.2
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
route53 = route53 =
route53resolver = route53resolver =
s3 = s3 =
PyYAML>=5.1 PyYAML>=5.1
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
s3crc32c = s3crc32c =
PyYAML>=5.1 PyYAML>=5.1
py-partiql-parser==0.5.0 py-partiql-parser==0.5.1
crc32c crc32c
s3control = s3control =
sagemaker = sagemaker =

View File

@ -334,6 +334,75 @@ def test_update_data(table_name=None):
assert item2 in items assert item2 in items
@mock_aws
def test_batch_update__not_enough_parameters():
ddb_cli = boto3.client("dynamodb", "us-east-1")
ddb_res = boto3.resource("dynamodb", "us-east-1")
ddb_res.create_table(
TableName="users",
KeySchema=[{"AttributeName": "username", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "username", "AttributeType": "S"}],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
statements = [
{
"Statement": 'UPDATE users SET "first_name" = ?, "last_name" = ? WHERE "username"= ?',
"Parameters": [{"S": "test5"}, {"S": "test6"}],
}
]
resp = ddb_cli.batch_execute_statement(Statements=statements)["Responses"]
assert resp == [
{
"Error": {
"Code": "ValidationError",
"Message": "Number of parameters in request and statement don't match.",
}
}
]
@mock_aws
def test_batch_update():
ddb_cli = boto3.client("dynamodb", "us-east-1")
ddb_res = boto3.resource("dynamodb", "us-east-1")
table = ddb_res.create_table(
TableName="users",
KeySchema=[{"AttributeName": "username", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "username", "AttributeType": "S"}],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
table.put_item(
Item={"username": "XXXX", "first_name": "test1", "last_name": "test2"}
)
table.put_item(
Item={"username": "YYYY", "first_name": "test3", "last_name": "test4"}
)
statements = [
{
"Statement": 'UPDATE users SET "first_name" = ?, "last_name" = ? WHERE "username"= ?',
"Parameters": [{"S": "test5"}, {"S": "test6"}, {"S": "XXXX"}],
},
{"Statement": "DELETE FROM users WHERE username='YYYY'"},
{"Statement": "INSERT INTO users value {'username': 'new'}"},
]
response = ddb_cli.batch_execute_statement(Statements=statements)["Responses"]
assert response == [
{"TableName": "users"},
{"TableName": "users"},
{"TableName": "users"},
]
users = ddb_res.Table("users").scan()["Items"]
assert len(users) == 2
# Changed
assert {"username": "XXXX", "first_name": "test5", "last_name": "test6"} in users
# New
assert {"username": "new"} in users
@pytest.mark.aws_verified @pytest.mark.aws_verified
@dynamodb_aws_verified() @dynamodb_aws_verified()
def test_delete_data(table_name=None): def test_delete_data(table_name=None):