1use crate::actions::helpers;
2use crate::errors::{DynoxideError, Result};
3use crate::expressions;
4use crate::storage_backend::StorageBackend;
5use crate::types::{AttributeValue, Item};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9const MAX_RESPONSE_SIZE: usize = 1_048_576;
11
12#[derive(Debug, Default, Deserialize)]
14struct QueryRequestRaw {
15 #[serde(rename = "TableName", default)]
16 table_name: Option<String>,
17 #[serde(rename = "KeyConditionExpression", default)]
18 key_condition_expression: Option<String>,
19 #[serde(rename = "FilterExpression", default)]
20 filter_expression: Option<String>,
21 #[serde(rename = "ProjectionExpression", default)]
22 projection_expression: Option<String>,
23 #[serde(rename = "ExpressionAttributeNames", default)]
24 expression_attribute_names: Option<HashMap<String, String>>,
25 #[serde(rename = "ExpressionAttributeValues", default)]
26 expression_attribute_values: Option<HashMap<String, AttributeValue>>,
27 #[serde(rename = "ScanIndexForward", default = "default_true")]
28 scan_index_forward: bool,
29 #[serde(rename = "Limit", default)]
30 limit: Option<usize>,
31 #[serde(rename = "ExclusiveStartKey", default)]
32 exclusive_start_key: Option<serde_json::Value>,
33 #[serde(rename = "Select", default)]
34 select: Option<String>,
35 #[serde(rename = "ConsistentRead", default)]
36 consistent_read: Option<bool>,
37 #[serde(rename = "IndexName", default)]
38 index_name: Option<String>,
39 #[serde(rename = "ReturnConsumedCapacity", default)]
40 return_consumed_capacity: Option<String>,
41 #[serde(rename = "KeyConditions", default)]
42 key_conditions: Option<serde_json::Value>,
43 #[serde(rename = "AttributesToGet", default)]
44 attributes_to_get: Option<Vec<String>>,
45 #[serde(rename = "QueryFilter", default)]
46 query_filter: Option<serde_json::Value>,
47 #[serde(rename = "ConditionalOperator", default)]
48 conditional_operator: Option<String>,
49}
50
51fn default_true() -> bool {
52 true
53}
54
55#[derive(Debug, Default)]
56pub struct QueryRequest {
57 pub table_name: String,
58 pub key_condition_expression: Option<String>,
59 pub filter_expression: Option<String>,
60 pub projection_expression: Option<String>,
61 pub expression_attribute_names: Option<HashMap<String, String>>,
62 pub expression_attribute_values: Option<HashMap<String, AttributeValue>>,
63 pub scan_index_forward: bool,
64 pub limit: Option<usize>,
65 pub exclusive_start_key: Option<HashMap<String, AttributeValue>>,
66 pub select: Option<String>,
67 pub consistent_read: Option<bool>,
68 pub index_name: Option<String>,
69 pub return_consumed_capacity: Option<String>,
70 pub key_conditions: Option<serde_json::Value>,
71 pub attributes_to_get: Option<Vec<String>>,
72 pub query_filter: Option<serde_json::Value>,
73 pub conditional_operator: Option<String>,
74 pub exclusive_start_key_raw: Option<serde_json::Value>,
79}
80
81impl<'de> serde::Deserialize<'de> for QueryRequest {
82 fn deserialize<D: serde::Deserializer<'de>>(
83 deserializer: D,
84 ) -> std::result::Result<Self, D::Error> {
85 let raw = QueryRequestRaw::deserialize(deserializer)?;
86 use crate::validation::{
87 TableNameContext, format_validation_errors, table_name_constraint_errors,
88 };
89
90 let mut errors = Vec::new();
91 errors.extend(table_name_constraint_errors(
92 raw.table_name.as_deref(),
93 TableNameContext::ReadWrite,
94 ));
95 let table_name = raw.table_name.unwrap_or_default();
96
97 if let Some(ref rcc) = raw.return_consumed_capacity {
99 if !["INDEXES", "TOTAL", "NONE"].contains(&rcc.as_str()) {
100 errors.push(format!(
101 "Value '{}' at 'returnConsumedCapacity' failed to satisfy constraint: \
102 Member must satisfy enum value set: [INDEXES, TOTAL, NONE]",
103 rcc
104 ));
105 }
106 }
107
108 if let Some(ref sel) = raw.select {
110 if ![
111 "ALL_ATTRIBUTES",
112 "ALL_PROJECTED_ATTRIBUTES",
113 "COUNT",
114 "SPECIFIC_ATTRIBUTES",
115 ]
116 .contains(&sel.as_str())
117 {
118 errors.push(format!(
119 "Value '{}' at 'select' failed to satisfy constraint: \
120 Member must satisfy enum value set: [SPECIFIC_ATTRIBUTES, COUNT, ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES]",
121 sel
122 ));
123 }
124 }
125
126 if let Some(limit) = raw.limit {
131 if limit == 0 {
132 errors.push(
133 "Value at 'Limit' failed to satisfy constraint: \
134 Member must have value greater than or equal to 1"
135 .to_string(),
136 );
137 }
138 }
139
140 if let Some(msg) = format_validation_errors(&errors) {
141 return Err(serde::de::Error::custom(format!("VALIDATION:{}", msg)));
142 }
143
144 Ok(QueryRequest {
145 table_name,
146 key_condition_expression: raw.key_condition_expression,
147 filter_expression: raw.filter_expression,
148 projection_expression: raw.projection_expression,
149 expression_attribute_names: raw.expression_attribute_names,
150 expression_attribute_values: raw.expression_attribute_values,
151 scan_index_forward: raw.scan_index_forward,
152 limit: raw.limit,
153 exclusive_start_key: None,
154 select: raw.select,
155 consistent_read: raw.consistent_read,
156 index_name: raw.index_name,
157 return_consumed_capacity: raw.return_consumed_capacity,
158 key_conditions: raw.key_conditions,
159 attributes_to_get: raw.attributes_to_get,
160 query_filter: raw.query_filter,
161 conditional_operator: raw.conditional_operator,
162 exclusive_start_key_raw: raw.exclusive_start_key,
163 })
164 }
165}
166
167#[derive(Debug, Default, Serialize)]
168pub struct QueryResponse {
169 #[serde(rename = "Items", skip_serializing_if = "Option::is_none")]
170 pub items: Option<Vec<Item>>,
171 #[serde(rename = "Count")]
172 pub count: usize,
173 #[serde(rename = "ScannedCount")]
174 pub scanned_count: usize,
175 #[serde(rename = "LastEvaluatedKey", skip_serializing_if = "Option::is_none")]
176 pub last_evaluated_key: Option<HashMap<String, AttributeValue>>,
177 #[serde(rename = "ConsumedCapacity", skip_serializing_if = "Option::is_none")]
178 pub consumed_capacity: Option<crate::types::ConsumedCapacity>,
179}
180
181pub async fn execute<S: StorageBackend>(
182 storage: &S,
183 mut request: QueryRequest,
184) -> Result<QueryResponse> {
185 crate::validation::validate_table_name(&request.table_name)?;
187
188 {
191 let mut non_expr = Vec::new();
192 let mut expr = Vec::new();
193 if request.attributes_to_get.is_some() {
194 non_expr.push("AttributesToGet");
195 }
196 if request.query_filter.is_some()
197 && request.query_filter.as_ref().is_some_and(|v| !v.is_null())
198 {
199 non_expr.push("QueryFilter");
200 }
201 if request.conditional_operator.is_some() {
202 non_expr.push("ConditionalOperator");
203 }
204 if request.key_conditions.is_some()
205 && request
206 .key_conditions
207 .as_ref()
208 .is_some_and(|v| !v.is_null())
209 {
210 non_expr.push("KeyConditions");
211 }
212 if request.projection_expression.is_some() {
213 expr.push("ProjectionExpression");
214 }
215 if request.filter_expression.is_some() {
216 expr.push("FilterExpression");
217 }
218 if request.key_condition_expression.is_some() {
219 expr.push("KeyConditionExpression");
220 }
221 let no_raw_eav: Option<serde_json::Value> = None;
222 let ctx = helpers::ExpressionParamContext {
223 non_expression_params: non_expr,
224 expression_params: expr,
225 all_expression_param_names: vec!["FilterExpression", "KeyConditionExpression"],
226 expression_attribute_names: &request.expression_attribute_names,
227 expression_attribute_values: &request.expression_attribute_values,
228 expression_attribute_values_raw: &no_raw_eav,
229 };
230 helpers::validate_expression_params(&ctx)?;
231 }
232
233 helpers::validate_filter_conditions_raw(request.query_filter.as_ref(), "QueryFilter")?;
235 helpers::validate_filter_conditions_raw(request.key_conditions.as_ref(), "KeyConditions")?;
236
237 helpers::validate_filter_condition_args(request.query_filter.as_ref())?;
239 helpers::validate_filter_condition_args(request.key_conditions.as_ref())?;
240
241 if let Some(ref attrs) = request.attributes_to_get {
243 helpers::validate_attributes_to_get_no_duplicates(attrs)?;
244 }
245
246 let exclusive_start_key = if let Some(ref esk_val) = request.exclusive_start_key_raw {
248 Some(helpers::parse_exclusive_start_key(esk_val)?)
249 } else {
250 request.exclusive_start_key.clone()
251 };
252
253 if let Some(ref kce) = request.key_condition_expression {
257 if kce.is_empty() {
258 return Err(DynoxideError::ValidationException(
259 "Invalid KeyConditionExpression: The expression can not be empty;".to_string(),
260 ));
261 }
262 }
263 if let Some(ref fe) = request.filter_expression {
264 if fe.is_empty() {
265 if request.query_filter.is_none() || request.filter_expression.as_deref() == Some("") {
266 return Err(DynoxideError::ValidationException(
267 "Invalid FilterExpression: The expression can not be empty;".to_string(),
268 ));
269 }
270 } else {
271 let parsed_fe = expressions::condition::parse(fe).map_err(|e| {
272 DynoxideError::ValidationException(format!("Invalid FilterExpression: {e}"))
273 })?;
274 if let Err(e) = expressions::condition::validate_name_refs(
277 &parsed_fe,
278 &request.expression_attribute_names,
279 ) {
280 return Err(DynoxideError::ValidationException(format!(
281 "Invalid FilterExpression: {e}"
282 )));
283 }
284 if let Err(e) = expressions::condition::validate_operand_semantics(
285 &parsed_fe,
286 &request.expression_attribute_names,
287 &request.expression_attribute_values,
288 ) {
289 return Err(DynoxideError::ValidationException(format!(
290 "Invalid FilterExpression: {e}"
291 )));
292 }
293 }
294 }
295 if let Some(ref pe) = request.projection_expression {
296 if pe.is_empty() {
297 return Err(DynoxideError::ValidationException(
298 "Invalid ProjectionExpression: The expression can not be empty;".to_string(),
299 ));
300 }
301 }
302
303 if request.select.as_deref() == Some("SPECIFIC_ATTRIBUTES")
307 && request.projection_expression.is_none()
308 && request.attributes_to_get.is_none()
309 {
310 return Err(DynoxideError::ValidationException(
311 "1 validation error detected: Must specify the AttributesToGet or ProjectionExpression when choosing to get SPECIFIC_ATTRIBUTES"
312 .to_string(),
313 ));
314 }
315
316 if request.projection_expression.is_some() {
318 if let Some(select) = request.select.as_deref() {
319 if select != "SPECIFIC_ATTRIBUTES" {
320 let target = if select == "COUNT" {
322 "only the Count"
323 } else {
324 select
325 };
326 return Err(DynoxideError::ValidationException(format!(
327 "Cannot specify the ProjectionExpression when choosing to get {target}"
328 )));
329 }
330 }
331 }
332
333 if request.select.as_deref() == Some("ALL_PROJECTED_ATTRIBUTES") && request.index_name.is_none()
335 {
336 return Err(DynoxideError::ValidationException(
337 "ALL_PROJECTED_ATTRIBUTES can be used only when Querying using an IndexName"
338 .to_string(),
339 ));
340 }
341
342 if let Some(ref kce) = request.key_condition_expression {
344 if !kce.is_empty() {
345 let temp_tracker = crate::expressions::TrackedExpressionAttributes::new(
347 &request.expression_attribute_names,
348 &request.expression_attribute_values,
349 );
350 if let Err(e) = expressions::key_condition::parse(kce, &temp_tracker) {
351 return Err(DynoxideError::ValidationException(e));
352 }
353 }
354 }
355
356 let meta = helpers::require_table_for_item_op(storage, &request.table_name).await?;
357 let table_key_schema = helpers::parse_key_schema(&meta)?;
358
359 let effective_pk_for_kc = if let Some(ref index_name) = request.index_name {
363 if let Some((pk, _)) = request
364 .index_name
365 .as_ref()
366 .and_then(|idx| super::lsi::parse_lsi_key_schema(&meta, idx).ok())
367 {
368 pk
369 } else if let Ok((pk, _)) = super::gsi::parse_gsi_key_schema(&meta, index_name) {
370 pk
371 } else {
372 table_key_schema.partition_key.clone()
373 }
374 } else {
375 table_key_schema.partition_key.clone()
376 };
377
378 if request.key_condition_expression.is_none() {
380 if let Some(ref kc_val) = request.key_conditions {
381 if let Ok(kc) =
382 serde_json::from_value::<HashMap<String, helpers::KeyCondition>>(kc_val.clone())
383 {
384 if !kc.is_empty() {
385 let converted =
386 helpers::convert_key_conditions(&kc, Some(&effective_pk_for_kc))?;
387 request.key_condition_expression = Some(converted.expression);
388 let expr_values = request
389 .expression_attribute_values
390 .get_or_insert_with(HashMap::new);
391 expr_values.extend(converted.attribute_values);
392 let expr_names = request
393 .expression_attribute_names
394 .get_or_insert_with(HashMap::new);
395 expr_names.extend(converted.attribute_names);
396 }
397 }
398 }
399 }
400
401 if request.filter_expression.is_none() {
403 if let Some(ref qf_val) = request.query_filter {
404 if let Ok(qf) =
405 serde_json::from_value::<HashMap<String, helpers::FilterCondition>>(qf_val.clone())
406 {
407 if !qf.is_empty() {
408 let converted = helpers::convert_filter_conditions(
409 &qf,
410 request.conditional_operator.as_deref(),
411 )?;
412 if !converted.expression.is_empty() {
413 request.filter_expression = Some(converted.expression);
414 let expr_values = request
415 .expression_attribute_values
416 .get_or_insert_with(HashMap::new);
417 expr_values.extend(converted.attribute_values);
418 let expr_names = request
419 .expression_attribute_names
420 .get_or_insert_with(HashMap::new);
421 expr_names.extend(converted.attribute_names);
422 }
423 }
424 }
425 }
426 }
427
428 let legacy_projection = if request.projection_expression.is_none() {
430 request
431 .attributes_to_get
432 .as_ref()
433 .map(|attrs| helpers::attributes_to_get_to_projection(attrs))
434 } else {
435 None
436 };
437
438 let key_condition_expression = request.key_condition_expression.as_deref().ok_or_else(|| {
440 DynoxideError::ValidationException(
441 "Either the KeyConditions or KeyConditionExpression parameter must be specified in the request."
442 .to_string(),
443 )
444 })?;
445 let key_condition_expression = key_condition_expression.to_string();
446
447 let lsi_keys = request
449 .index_name
450 .as_ref()
451 .and_then(|idx| super::lsi::parse_lsi_key_schema(&meta, idx).ok());
452 let is_lsi = lsi_keys.is_some();
453
454 if request.consistent_read.unwrap_or(false) && request.index_name.is_some() && !is_lsi {
456 return Err(DynoxideError::ValidationException(
457 "Consistent reads are not supported on global secondary indexes".to_string(),
458 ));
459 }
460
461 let index_projection_type = if let Some(ref index_name) = request.index_name {
463 if is_lsi {
464 super::lsi::parse_lsi_defs(&meta)?
465 .into_iter()
466 .find(|l| l.index_name == *index_name)
467 .map(|l| l.projection_type)
468 } else {
469 super::gsi::parse_gsi_defs(&meta)?
470 .into_iter()
471 .find(|g| g.index_name == *index_name)
472 .map(|g| g.projection_type)
473 }
474 } else {
475 None
476 };
477
478 let (effective_pk, effective_sk) = if let Some(ref index_name) = request.index_name {
479 if let Some(keys) = lsi_keys {
480 keys
481 } else {
482 super::gsi::parse_gsi_key_schema(&meta, index_name)?
483 }
484 } else {
485 (
486 table_key_schema.partition_key.clone(),
487 table_key_schema.sort_key.clone(),
488 )
489 };
490
491 if let Some(ref esk) = exclusive_start_key {
493 helpers::validate_esk_count_and_index_keys(
495 esk,
496 &meta,
497 request.index_name.as_deref(),
498 "The provided starting key is invalid",
499 )?;
500 helpers::validate_esk_table_keys(esk, &meta)?;
502 }
503
504 let tracker = crate::expressions::TrackedExpressionAttributes::new(
506 &request.expression_attribute_names,
507 &request.expression_attribute_values,
508 );
509
510 let key_cond = expressions::key_condition::parse(&key_condition_expression, &tracker)
512 .map_err(DynoxideError::ValidationException)?;
513
514 if key_cond.pk_name != effective_pk {
516 return Err(DynoxideError::ValidationException(format!(
517 "Query condition missed key schema element: {}",
518 effective_pk
519 )));
520 }
521
522 let resolved = expressions::key_condition::resolve_values(&key_cond, &tracker)
524 .map_err(DynoxideError::ValidationException)?;
525
526 let pk_str = resolved.pk_value.to_key_string().ok_or_else(|| {
528 DynoxideError::ValidationException(
529 "Cannot convert partition key value to string".to_string(),
530 )
531 })?;
532
533 let mut sk_sql_parts = Vec::new();
535 let mut sk_param_values = Vec::new();
536
537 if let Some(ref sk_cond) = resolved.sk_condition {
538 if let Some(ref eff_sk) = effective_sk {
540 if sk_cond.sk_name() != eff_sk {
541 return Err(DynoxideError::ValidationException(format!(
542 "Query condition missed key schema element: {eff_sk}"
543 )));
544 }
545 } else {
546 return Err(DynoxideError::ValidationException(
547 "Query filter contains a sort key condition but the table has no sort key"
548 .to_string(),
549 ));
550 }
551
552 let conditions = sk_cond.to_sql_conditions();
553 for (i, (op, val)) in conditions.iter().enumerate() {
554 let param_idx = i + 2; if op == "LIKE" {
556 sk_sql_parts.push(format!("AND sk LIKE ?{param_idx} ESCAPE '\\'"));
557 } else {
558 sk_sql_parts.push(format!("AND sk {op} ?{param_idx}"));
559 }
560 sk_param_values.push(val.clone());
561 }
562 }
563
564 let mut effective_key_attrs = vec![effective_pk.clone()];
567 if let Some(ref sk) = effective_sk {
568 effective_key_attrs.push(sk.clone());
569 }
570
571 if let Some(ref qf_val) = request.query_filter {
573 if let Some(obj) = qf_val.as_object() {
574 for attr_name in obj.keys() {
575 if effective_key_attrs.contains(attr_name) {
576 return Err(DynoxideError::ValidationException(format!(
577 "QueryFilter can only contain non-primary key attributes: \
578 Primary key attribute: {attr_name}"
579 )));
580 }
581 }
582 }
583 }
584
585 if request.query_filter.is_none() {
588 if let Some(ref fe) = request.filter_expression {
589 if let Ok(parsed_fe) = expressions::condition::parse(fe) {
590 let top_attrs = expressions::condition::extract_top_level_attributes(
591 &parsed_fe,
592 &request.expression_attribute_names,
593 );
594 for attr in &top_attrs {
595 if effective_key_attrs.contains(attr) {
596 return Err(DynoxideError::ValidationException(format!(
597 "Filter Expression can only contain non-primary key attributes: \
598 Primary key attribute: {attr}"
599 )));
600 }
601 }
602 let mut index_key_attrs = Vec::new();
605 if request.index_name.is_some() {
606 if !effective_key_attrs
608 .iter()
609 .any(|k| k == &table_key_schema.partition_key)
610 {
611 }
613 for k in &effective_key_attrs {
615 if ![table_key_schema.partition_key.clone()]
616 .iter()
617 .chain(table_key_schema.sort_key.iter())
618 .any(|tk| tk == k)
619 {
620 index_key_attrs.push(k.clone());
621 }
622 }
623 }
624 let base_key_attrs: Vec<String> = {
625 let mut v = vec![table_key_schema.partition_key.clone()];
626 if let Some(ref sk) = table_key_schema.sort_key {
627 v.push(sk.clone());
628 }
629 v
630 };
631 if let Some((attr, is_index)) = expressions::condition::check_non_scalar_key_access(
632 &parsed_fe,
633 &request.expression_attribute_names,
634 &base_key_attrs,
635 &index_key_attrs,
636 ) {
637 let prefix = if is_index { "IndexKey" } else { "Key" };
638 return Err(DynoxideError::ValidationException(format!(
639 "Key attributes must be scalars; \
640 list random access '[]' and map lookup '.' are not allowed: {prefix}: {attr}"
641 )));
642 }
643 }
644 }
645 }
646
647 let is_index_query = request.index_name.is_some();
648
649 let start_sk = if let Some(ref esk) = exclusive_start_key {
653 if let Some(ref sk_name) = effective_sk {
654 esk.get(sk_name).and_then(|v| v.to_key_string())
655 } else if is_index_query {
656 Some(String::new())
658 } else {
659 None
660 }
661 } else {
662 None
663 };
664
665 let (start_base_pk, start_base_sk) = if is_index_query {
668 if let Some(ref esk) = exclusive_start_key {
669 let base_pk = esk
670 .get(&table_key_schema.partition_key)
671 .and_then(|v| v.to_key_string());
672 let base_sk = if let Some(sk_name) = table_key_schema.sort_key.as_ref() {
680 esk.get(sk_name).and_then(|v| v.to_key_string())
681 } else {
682 Some(String::new())
683 };
684 (base_pk, base_sk)
685 } else {
686 (None, None)
687 }
688 } else {
689 (None, None)
690 };
691
692 let is_select_all_attributes = request
695 .select
696 .as_deref()
697 .map(|s| s.eq_ignore_ascii_case("ALL_ATTRIBUTES"))
698 .unwrap_or(false);
699 let fetch_from_base_table = if is_select_all_attributes {
700 if let Some(ref proj_type) = index_projection_type {
701 if *proj_type != crate::types::ProjectionType::ALL {
702 if !is_lsi {
703 return Err(DynoxideError::ValidationException(format!(
704 "One or more parameter values were invalid: \
705 Select type ALL_ATTRIBUTES is not supported for global secondary index {} \
706 because its projection type is not ALL",
707 request.index_name.as_deref().unwrap_or("")
708 )));
709 }
710 true
712 } else {
713 false
714 }
715 } else {
716 false
717 }
718 } else {
719 false
720 };
721
722 let sk_condition_sql = if sk_sql_parts.is_empty() {
724 None
725 } else {
726 Some(sk_sql_parts.join(" "))
727 };
728
729 let fetch_limit = request.limit;
730 let sk_params_refs: Vec<&str> = sk_param_values.iter().map(|s| s.as_str()).collect();
731
732 let query_params = crate::storage::QueryParams {
734 sk_condition: sk_condition_sql.as_deref(),
735 sk_params: &sk_params_refs,
736 forward: request.scan_index_forward,
737 limit: fetch_limit,
738 exclusive_start_sk: start_sk.as_deref(),
739 exclusive_start_base_pk: start_base_pk.as_deref(),
740 exclusive_start_base_sk: start_base_sk.as_deref(),
741 };
742 let rows = if let Some(ref index_name) = request.index_name {
743 if is_lsi {
744 storage
745 .query_lsi_items(&request.table_name, index_name, &pk_str, &query_params)
746 .await?
747 } else {
748 storage
749 .query_gsi_items(&request.table_name, index_name, &pk_str, &query_params)
750 .await?
751 }
752 } else {
753 storage
754 .query_items(&request.table_name, &pk_str, &query_params)
755 .await?
756 };
757
758 let filter_expr = request
760 .filter_expression
761 .as_ref()
762 .map(|expr| expressions::condition::parse(expr))
763 .transpose()
764 .map_err(DynoxideError::ValidationException)?;
765
766 let projection = if let Some(ref proj_expr) = request.projection_expression {
768 Some(
769 expressions::projection::parse(proj_expr)
770 .map_err(DynoxideError::ValidationException)?,
771 )
772 } else {
773 legacy_projection.clone()
774 };
775
776 if let Some(ref filter) = filter_expr {
778 tracker.track_condition_expr(filter);
779 }
780 if let Some(ref proj) = projection {
781 tracker.track_projection_expr(proj);
782 expressions::projection::validate(proj, &tracker)
785 .map_err(DynoxideError::ValidationException)?;
786 }
787
788 let loop_tracker = crate::expressions::TrackedExpressionAttributes::without_tracking(
790 &request.expression_attribute_names,
791 &request.expression_attribute_values,
792 );
793
794 let is_count = request
796 .select
797 .as_deref()
798 .map(|s| s.eq_ignore_ascii_case("COUNT"))
799 .unwrap_or(false);
800
801 let mut key_attrs = vec![effective_pk.clone()];
803 if let Some(ref sk) = effective_sk {
804 key_attrs.push(sk.clone());
805 }
806 if request.index_name.is_some() {
808 if !key_attrs.contains(&table_key_schema.partition_key) {
809 key_attrs.push(table_key_schema.partition_key.clone());
810 }
811 if let Some(ref sk) = table_key_schema.sort_key {
812 if !key_attrs.contains(sk) {
813 key_attrs.push(sk.clone());
814 }
815 }
816 }
817
818 let mut items = Vec::new();
819 let mut scanned_count = 0;
820 let mut filtered_count = 0;
821 let mut cumulative_size = 0;
822 let mut last_evaluated_item: Option<Item> = None;
823 let mut truncated_by_size = false;
824
825 let mut base_table_cumulative_size = 0usize;
828 let mut index_cumulative_size = 0usize;
829
830 for (_pk, _sk, item_json) in &rows {
831 let index_item: Item = serde_json::from_str(item_json).map_err(|e| {
832 DynoxideError::InternalServerError(format!("Bad item JSON in storage: {e}"))
833 })?;
834
835 index_cumulative_size += crate::types::item_size(&index_item);
839 let item = if fetch_from_base_table {
840 let base_pk = index_item
841 .get(&table_key_schema.partition_key)
842 .and_then(|v| v.to_key_string())
843 .unwrap_or_default();
844 let base_sk = table_key_schema
845 .sort_key
846 .as_ref()
847 .and_then(|sk_name| index_item.get(sk_name))
848 .and_then(|v| v.to_key_string())
849 .unwrap_or_default();
850 if let Some(full_json) = storage
851 .get_item(&request.table_name, &base_pk, &base_sk)
852 .await?
853 {
854 let full_item: Item = serde_json::from_str(&full_json).map_err(|e| {
855 DynoxideError::InternalServerError(format!("Bad item JSON: {e}"))
856 })?;
857 base_table_cumulative_size += crate::types::item_size(&full_item);
858 full_item
859 } else {
860 index_item.clone()
861 }
862 } else {
863 index_item.clone()
864 };
865
866 scanned_count += 1;
867
868 let item_size = crate::types::item_size(&item);
871 if cumulative_size + item_size > MAX_RESPONSE_SIZE && scanned_count > 1 {
872 truncated_by_size = true;
873 break;
874 }
875 cumulative_size += item_size;
876
877 if let Some(ref filter) = filter_expr {
879 let passes = expressions::condition::evaluate(filter, &item, &loop_tracker)
880 .map_err(DynoxideError::ValidationException)?;
881 if !passes {
882 last_evaluated_item = Some(index_item);
883 continue;
884 }
885 }
886
887 filtered_count += 1;
888
889 let result_item = if let Some(ref proj) = projection {
892 let no_keys: &[String] = &[];
893 expressions::projection::apply(&item, proj, &loop_tracker, no_keys)
894 .map_err(DynoxideError::ValidationException)?
895 } else {
896 item
897 };
898
899 last_evaluated_item = Some(index_item);
900 if !is_count {
901 items.push(result_item);
902 }
903 }
904
905 tracker.check_unused()?;
907
908 let count = if is_count {
909 filtered_count
910 } else {
911 items.len()
912 };
913
914 let has_more = truncated_by_size
917 || (fetch_limit.is_some() && scanned_count >= fetch_limit.unwrap_or(usize::MAX));
918
919 let is_gsi_query = request.index_name.is_some() && !is_lsi;
924 let last_evaluated_key = if has_more {
925 last_evaluated_item.map(|item| {
926 let mut key = build_last_evaluated_key(&item, &effective_pk, effective_sk.as_deref());
927 if is_lsi {
929 if let Some(tsk) = table_key_schema.sort_key.as_deref() {
930 if !key.contains_key(tsk) {
931 if let Some(v) = item.get(tsk) {
932 key.insert(tsk.to_string(), v.clone());
933 }
934 }
935 }
936 }
937 if is_gsi_query {
939 if !key.contains_key(&table_key_schema.partition_key) {
940 if let Some(v) = item.get(&table_key_schema.partition_key) {
941 key.insert(table_key_schema.partition_key.clone(), v.clone());
942 }
943 }
944 if let Some(ref tsk) = table_key_schema.sort_key {
945 if !key.contains_key(tsk) {
946 if let Some(v) = item.get(tsk) {
947 key.insert(tsk.clone(), v.clone());
948 }
949 }
950 }
951 }
952 key
953 })
954 } else {
955 None
956 };
957
958 let is_gsi = is_gsi_query;
960 let consistent = request.consistent_read.unwrap_or(false);
961 let consumed_capacity = if is_gsi {
962 let mut gsi_units = std::collections::HashMap::new();
963 gsi_units.insert(
964 request.index_name.as_ref().unwrap().clone(),
965 crate::types::read_capacity_units_with_consistency(cumulative_size, consistent),
966 );
967 crate::types::consumed_capacity_with_indexes(
968 &request.table_name,
969 0.0,
970 &gsi_units,
971 &request.return_consumed_capacity,
972 )
973 } else if is_lsi {
974 let (table_cap, lsi_cap) = if fetch_from_base_table {
977 let table_rcu = crate::types::read_capacity_units_with_consistency(
978 base_table_cumulative_size,
979 consistent,
980 );
981 let lsi_rcu = crate::types::read_capacity_units_with_consistency(
982 index_cumulative_size,
983 consistent,
984 );
985 (table_rcu, lsi_rcu)
986 } else {
987 (
988 0.0,
989 crate::types::read_capacity_units_with_consistency(cumulative_size, consistent),
990 )
991 };
992 let mut lsi_units = std::collections::HashMap::new();
993 lsi_units.insert(request.index_name.as_ref().unwrap().clone(), lsi_cap);
994 crate::types::consumed_capacity_with_secondary_indexes(
995 &request.table_name,
996 table_cap,
997 &std::collections::HashMap::new(),
998 &lsi_units,
999 &request.return_consumed_capacity,
1000 )
1001 } else {
1002 crate::types::consumed_capacity(
1003 &request.table_name,
1004 crate::types::read_capacity_units_with_consistency(cumulative_size, consistent),
1005 &request.return_consumed_capacity,
1006 )
1007 };
1008
1009 Ok(QueryResponse {
1010 items: if is_count { None } else { Some(items) },
1011 count,
1012 scanned_count,
1013 last_evaluated_key,
1014 consumed_capacity,
1015 })
1016}
1017
1018fn build_last_evaluated_key(
1019 item: &Item,
1020 pk_name: &str,
1021 sk_name: Option<&str>,
1022) -> HashMap<String, AttributeValue> {
1023 let mut key = HashMap::new();
1024 if let Some(pk_val) = item.get(pk_name) {
1025 key.insert(pk_name.to_string(), pk_val.clone());
1026 }
1027 if let Some(sk) = sk_name {
1028 if let Some(sk_val) = item.get(sk) {
1029 key.insert(sk.to_string(), sk_val.clone());
1030 }
1031 }
1032 key
1033}