Skip to main content

dynoxide/actions/
batch_execute_statement.rs

1use crate::errors::{DynoxideError, Result};
2use crate::partiql;
3use crate::storage::Storage;
4use crate::types::{AttributeValue, Item};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Default, Deserialize)]
8pub struct BatchExecuteStatementRequest {
9    #[serde(rename = "Statements")]
10    pub statements: Vec<BatchStatementRequest>,
11}
12
13#[derive(Debug, Default, Deserialize)]
14pub struct BatchStatementRequest {
15    #[serde(rename = "Statement")]
16    pub statement: String,
17    #[serde(rename = "Parameters", default)]
18    pub parameters: Option<Vec<AttributeValue>>,
19}
20
21#[derive(Debug, Default, Serialize)]
22pub struct BatchExecuteStatementResponse {
23    #[serde(rename = "Responses")]
24    pub responses: Vec<BatchStatementResponse>,
25}
26
27#[derive(Debug, Default, Serialize)]
28pub struct BatchStatementResponse {
29    #[serde(rename = "Error", skip_serializing_if = "Option::is_none")]
30    pub error: Option<BatchStatementError>,
31    #[serde(rename = "Item", skip_serializing_if = "Option::is_none")]
32    pub item: Option<Item>,
33}
34
35#[derive(Debug, Default, Serialize)]
36pub struct BatchStatementError {
37    #[serde(rename = "Code")]
38    pub code: String,
39    #[serde(rename = "Message")]
40    pub message: String,
41}
42
43pub fn execute(
44    storage: &Storage,
45    request: BatchExecuteStatementRequest,
46) -> Result<BatchExecuteStatementResponse> {
47    if request.statements.is_empty() {
48        return Err(DynoxideError::ValidationException(
49            "1 validation error detected: Value '[]' at 'statements' failed to satisfy constraint: Member must have length greater than or equal to 1".to_string(),
50        ));
51    }
52
53    if request.statements.len() > 25 {
54        return Err(DynoxideError::ValidationException(
55            "Too many statements in BatchExecuteStatement; limit is 25".to_string(),
56        ));
57    }
58
59    let mut responses = Vec::with_capacity(request.statements.len());
60
61    for stmt_req in &request.statements {
62        let parsed = partiql::parser::parse(&stmt_req.statement);
63
64        let response = match parsed {
65            Err(e) => BatchStatementResponse {
66                error: Some(BatchStatementError {
67                    code: "ValidationException".to_string(),
68                    message: format!("Statement wasn't well formed, got error: {e}"),
69                }),
70                item: None,
71            },
72            Ok(stmt) => {
73                let params = stmt_req.parameters.as_deref().unwrap_or_default();
74                match partiql::executor::execute(storage, &stmt, params, None) {
75                    Ok(Some(items)) => {
76                        // For SELECT, return first item (batch returns single item per statement)
77                        BatchStatementResponse {
78                            error: None,
79                            item: items.into_iter().next(),
80                        }
81                    }
82                    Ok(None) => BatchStatementResponse {
83                        error: None,
84                        item: None,
85                    },
86                    Err(e) => BatchStatementResponse {
87                        error: Some(BatchStatementError {
88                            code: e.short_error_code().to_string(),
89                            message: e.to_string(),
90                        }),
91                        item: None,
92                    },
93                }
94            }
95        };
96
97        responses.push(response);
98    }
99
100    Ok(BatchExecuteStatementResponse { responses })
101}