From 0b4552399997f59ba13ffeac605b1a24cb8f3294 Mon Sep 17 00:00:00 2001 From: Lee Hannigan <29015211+LeeroyHannigan@users.noreply.github.com> Date: Mon, 2 Nov 2026 23:09:56 +0000 Subject: [PATCH] fix(scan): drop the FilterExpression suffix from the values message A Scan that carries ExpressionAttributeValues and no FilterExpression answered "ExpressionAttributeValues can only be specified when using expressions: FilterExpression is null". Amazon DynamoDB ends the message after "expressions", on a table that exists and on one that does not, with or without a ProjectionExpression or a ScanFilter (measured in us-east-1). handle_scan now passes an empty parameter list to validate_expression_param_usage, as Query does, so the message carries no suffix. The dual-target test expects the bare message. Fixes #722. --- crates/core/src/expression/resolver.rs | 12 ++++++------ crates/engine/src/scan.rs | 3 ++- tests/test_expression_param_usage.py | 8 ++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/core/src/expression/resolver.rs b/crates/core/src/expression/resolver.rs index fa14081ec..d9d9562e2 100755 --- a/crates/core/src/expression/resolver.rs +++ b/crates/core/src/expression/resolver.rs @@ -200,21 +200,21 @@ pub fn resolve_element_name<'a>( } /// Reject `ExpressionAttributeNames`/`Values` supplied with no expression that -/// can reference them. Matches Amazon `DynamoDB`: +/// can reference them: /// /// - names: `ExpressionAttributeNames can only be specified when using expressions` /// (no suffix), for every API. /// - values: `ExpressionAttributeValues can only be specified when using -/// expressions: is/are null`, listing the value-capable expression -/// parameter(s) of the API. +/// expressions`, followed by `: is null` or `: and are null` +/// when `value_expr_params` is not empty. /// /// `name_expr_present` is true when any expression that can carry a `#name` is /// present (projection, condition, filter, key condition, update). /// `value_expr_present` is true when any expression that can carry a `:value` is /// present (condition, filter, key condition, update; not projection). -/// `value_expr_params` is the API's static list of value-capable expression -/// parameter kinds, used to build the values suffix. An empty list yields no -/// suffix (matching Query). +/// `value_expr_params` lists the expression parameters the values message names +/// as null. Query and Scan pass an empty list, because the service's message for +/// them has no suffix. /// /// # Errors /// diff --git a/crates/engine/src/scan.rs b/crates/engine/src/scan.rs index 1c1de2274..555f77896 100755 --- a/crates/engine/src/scan.rs +++ b/crates/engine/src/scan.rs @@ -106,12 +106,13 @@ pub async fn handle_scan( .projection_expression .as_ref() .is_some_and(|s| !s.is_empty()); + // Scan emits no values suffix (measured in us-east-1, #722). extenddb_core::expression::validate_expression_param_usage( input.expression_attribute_names.as_ref(), has_proj_expr || has_filter_expr, input.expression_attribute_values.as_ref(), has_filter_expr, - &[ExpressionKind::Filter], + &[], )?; // ConditionalOperator requires a ScanFilter with two or more conditions. diff --git a/tests/test_expression_param_usage.py b/tests/test_expression_param_usage.py index 653f38d04..c09085161 100644 --- a/tests/test_expression_param_usage.py +++ b/tests/test_expression_param_usage.py @@ -5,8 +5,8 @@ Amazon DynamoDB rejects a request that provides ExpressionAttributeNames or ExpressionAttributeValues when there is no expression parameter that could -reference them. The names message is always suffix-free; the values message -names the null value-capable expression parameter(s) for that API. +reference them. The names message is always suffix-free. On Query and Scan +the values message is suffix-free as well: it ends after "expressions". Dual-target against Amazon DynamoDB and extenddb. """ @@ -82,7 +82,7 @@ def test_scan_values_no_expression(self, dynamodb_client, hash_table): TableName=hash_table, ExpressionAttributeValues={":v": {"S": "x"}}, ), - f"{VALUES_PREFIX}: FilterExpression is null", + VALUES_PREFIX, ) def test_delete_item_names_no_expression(self, dynamodb_client, hash_table): @@ -162,7 +162,7 @@ def test_query_names_no_expression(self, dynamodb_client, hash_table): ) def test_query_values_no_expression(self, dynamodb_client, hash_table): - # Query emits no values suffix, unlike Scan/Delete/Update. + # Query emits no values suffix, as Scan does. _expect_validation( lambda: dynamodb_client.query( TableName=hash_table,