Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
8e4aa22
fix(scan): refuse an empty ScanFilter mix and AttributesToGet, check …
LeeroyHannigan Oct 30, 2026
e20ffee
fix(scan): aggregate the AttributesToGet length with the range faults
LeeroyHannigan Oct 31, 2026
5741869
fix(scan): check the ExclusiveStartKey numbers at the key's own stage
LeeroyHannigan Nov 4, 2026
45705bf
fix(scan): check the ExclusiveStartKey at the service's two stages
LeeroyHannigan Nov 5, 2026
dba7827
fix(scan): follow the service's two check orders
LeeroyHannigan Nov 5, 2026
71c7543
fix(scan): check ScanFilter list values and order the value maps as t…
LeeroyHannigan Nov 5, 2026
3e4d822
Merge fix/563-scan-range-constraints into fix/564-scan-request-valida…
LeeroyHannigan Nov 6, 2026
a64d846
test(backup): pass the raw request body to dispatch in the SQLite bac…
LeeroyHannigan Nov 6, 2026
7e2987a
Merge fix/563-scan-range-constraints into fix/564-scan-request-valida…
LeeroyHannigan Nov 6, 2026
2639b8b
Merge fix/563-scan-range-constraints into fix/564-scan-request-valida…
LeeroyHannigan Nov 7, 2026
06a4a40
test(backup): pass an empty raw body to dispatch in the SQLite restor…
LeeroyHannigan Nov 7, 2026
f30a2cd
docs: name the three members whose order Scan reads from raw_body
LeeroyHannigan Nov 7, 2026
85bf667
Merge fix/563-scan-range-constraints into fix/564-scan-request-valida…
LeeroyHannigan Nov 9, 2026
5369a4b
fix(scan): pass an empty raw body to handle_scan from PartiQL SELECT
LeeroyHannigan Nov 9, 2026
941ead9
fix(partiql): accept a 32-level parameter in a SELECT of the whole table
LeeroyHannigan Nov 9, 2026
2e3e499
Merge fix/563-scan-range-constraints into fix/564-scan-request-valida…
LeeroyHannigan Nov 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion crates/engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,15 @@ impl OperationContext {

/// Dispatch an operation by name.
///
/// `raw_body` is the request text `body` was parsed from. `Scan` reads the
/// member order of its `ExpressionAttributeValues`, `ExclusiveStartKey` and
/// `ScanFilter` from it.
///
/// Returns the JSON response body and sideband metrics for the metrics collector.
pub async fn dispatch(
operation: &str,
body: serde_json::Value,
raw_body: &[u8],
ctx: &OperationContext,
server_addr: &str,
) -> Result<DispatchResult, DynamoDbError> {
Expand Down Expand Up @@ -502,7 +507,7 @@ pub async fn dispatch(
"DeleteItem" => handle_delete_item(body, ctx).await,
"UpdateItem" => handle_update_item(body, ctx).await,
"Query" => handle_query(body, ctx).await,
"Scan" => handle_scan(body, ctx).await,
"Scan" => handle_scan(body, raw_body, ctx).await,
"SearchVectors" => handle_search_vectors(body, ctx).await,
"BatchGetItem" => handle_batch_get_item(body, ctx).await,
"BatchWriteItem" => handle_batch_write_item(body, ctx).await,
Expand Down
7 changes: 4 additions & 3 deletions crates/engine/src/partiql/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
//! key, their key conditions and filters), checks it the way the service
//! does, composes the classic request the matching data-plane handler
//! already understands, drives [`crate::handle_query`] /
//! [`crate::handle_scan`], and shapes the response under `Items` with an
//! opaque, statement-bound `NextToken`. Every behaviour is justified by a
//! measurement against the live service, cited where it is implemented.
//! [`crate::scan::handle_partiql_scan`], and shapes the response under
//! `Items` with an opaque, statement-bound `NextToken`. Every behaviour is
//! justified by a measurement against the live service, cited where it is
//! implemented.
//!
//! A read of a table or an index by several disjuncts, or of a table by one
//! that names a whole item, takes the disjuncts one after another, the
Expand Down
2 changes: 1 addition & 1 deletion crates/engine/src/partiql/select/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub(super) async fn scan_request(
insert_names_values(&mut body, &bridge)?;
insert_common(&mut body, req, paging);

crate::handle_scan(Value::Object(body), ctx)
crate::scan::handle_partiql_scan(Value::Object(body), ctx)
.await
.map_err(crate::partiql::bare_expression_error)
}
Expand Down
27 changes: 17 additions & 10 deletions crates/engine/src/query_key_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,23 @@ pub(crate) fn shape_fault_first<T: DeserializeOwned>(
check: DynamoDbError,
) -> DynamoDbError {
match serde_json::from_value::<T>(body.clone()) {
Err(e) => match crate::deserialize_error(e) {
DynamoDbError::SerializationException(msg) if msg.contains(UNKNOWN_TYPE_DESCRIPTOR) => {
check
}
shape @ DynamoDbError::SerializationException(_) => shape,
_ => check,
},
Err(e) => {
let fault = crate::deserialize_error(e);
if is_shape_fault(&fault) { fault } else { check }
}
Ok(_) => check,
}
}

/// Whether `fault`, from [`crate::deserialize_error`], is a fault in the shape
/// of the body (a field of the wrong JSON type), which the service reports
/// ahead of every check, rather than an attribute value the deserializer
/// refuses with a `ValidationException` or an unknown type descriptor; see
/// [`shape_fault_first`].
pub(crate) fn is_shape_fault(fault: &DynamoDbError) -> bool {
matches!(fault, DynamoDbError::SerializationException(msg) if !msg.contains(UNKNOWN_TYPE_DESCRIPTOR))
}

/// The deserializer's text for an AttributeValue whose type descriptor is not
/// one of the service's.
const UNKNOWN_TYPE_DESCRIPTOR: &str = "unknown AttributeValue type descriptor";
Expand Down Expand Up @@ -205,7 +211,8 @@ pub(crate) fn non_empty(body: &Value, field: &str) -> bool {
}

/// The type descriptors an AttributeValue may carry.
const TYPE_DESCRIPTORS: &[&str] = &["S", "N", "B", "SS", "NS", "BS", "M", "L", "NULL", "BOOL"];
pub(crate) const TYPE_DESCRIPTORS: &[&str] =
&["S", "N", "B", "SS", "NS", "BS", "M", "L", "NULL", "BOOL"];

/// The legacy condition maps of `Scan` whose `AttributeValueList` entries
/// [`has_unreadable_attribute_value`] reads.
Expand Down Expand Up @@ -315,7 +322,7 @@ pub fn validate_expression_attribute_value_numbers(

/// Every number inside one attribute value (`N`, `NS` members, and the values
/// nested in `L` and `M`), with Unicode decimal digits folded for the check.
fn validate_value_numbers(value: &AttributeValue) -> Result<(), DynamoDbError> {
pub(crate) fn validate_value_numbers(value: &AttributeValue) -> Result<(), DynamoDbError> {
match value {
AttributeValue::N(n) => validate_number_text(n),
AttributeValue::NS(set) => set.iter().try_for_each(|n| validate_number_text(n)),
Expand All @@ -327,7 +334,7 @@ fn validate_value_numbers(value: &AttributeValue) -> Result<(), DynamoDbError> {

/// One number text against the number rules, with the service's `Query`
/// message for an empty value and the original text echoed for a malformed one.
fn validate_number_text(text: &str) -> Result<(), DynamoDbError> {
pub(crate) fn validate_number_text(text: &str) -> Result<(), DynamoDbError> {
let folded = crate::decimal_digits::fold_decimal_digits(text);
match extenddb_core::validation::number::validate_and_normalize_number(&folded) {
Ok(_) => Ok(()),
Expand Down
Loading
Loading