1use crate::expressions::condition::parse_raw_path;
6use crate::expressions::tokenizer::{
7 Token, TokenStream, near_window_parser, near_window_tokenizer, tokenize,
8};
9use crate::expressions::{
10 PathElement, TrackedExpressionAttributes, format_path_for_error, remove_path, resolve_path,
11 resolve_path_elements, set_path,
12};
13use crate::types::AttributeValue;
14use std::collections::HashMap;
15
16#[derive(Debug)]
18pub struct UpdateExpr {
19 pub set_actions: Vec<SetAction>,
20 pub remove_actions: Vec<Vec<PathElement>>,
21 pub add_actions: Vec<AddAction>,
22 pub delete_actions: Vec<DeleteAction>,
23}
24
25#[derive(Debug)]
27pub struct SetAction {
28 pub path: Vec<PathElement>,
29 pub value: SetValue,
30}
31
32#[derive(Debug)]
34pub enum SetValue {
35 Operand(SetOperand),
37 Plus(SetOperand, SetOperand),
39 Minus(SetOperand, SetOperand),
41}
42
43#[derive(Debug)]
45pub enum SetOperand {
46 Path(Vec<PathElement>),
47 ValueRef(String),
48 IfNotExists(Vec<PathElement>, Box<SetOperand>),
49 ListAppend(Box<SetOperand>, Box<SetOperand>),
50 Group(Box<SetValue>),
52}
53
54#[derive(Debug)]
56pub struct AddAction {
57 pub path: Vec<PathElement>,
58 pub value_ref: String,
59}
60
61#[derive(Debug)]
63pub struct DeleteAction {
64 pub path: Vec<PathElement>,
65 pub value_ref: String,
66}
67
68pub fn parse(expr: &str) -> Result<UpdateExpr, String> {
70 let tokens = match tokenize(expr) {
71 Ok(t) => t,
72 Err(err) => {
73 let bad = &expr[err.position..err.position + err.bad_len];
77 let near = near_window_tokenizer(expr, err.position);
78 return Err(format!(
79 r#"Invalid UpdateExpression: Syntax error; token: "{bad}", near: "{near}""#
80 ));
81 }
82 };
83 let mut stream = TokenStream::new(tokens);
84
85 let mut set_actions = Vec::new();
86 let mut remove_actions = Vec::new();
87 let mut add_actions = Vec::new();
88 let mut delete_actions = Vec::new();
89
90 let mut seen_set = false;
91 let mut seen_remove = false;
92 let mut seen_add = false;
93 let mut seen_delete = false;
94
95 while !stream.at_end() {
96 match stream.peek() {
97 Some(Token::Set) => {
98 if seen_set {
99 return Err("Invalid UpdateExpression: The \"SET\" section can only be used once in an update expression;".to_string());
100 }
101 seen_set = true;
102 stream.next();
103 parse_set_clause(&mut stream, &mut set_actions).map_err(wrap_syntax_error)?;
104 }
105 Some(Token::Remove) => {
106 if seen_remove {
107 return Err("Invalid UpdateExpression: The \"REMOVE\" section can only be used once in an update expression;".to_string());
108 }
109 seen_remove = true;
110 stream.next();
111 parse_remove_clause(&mut stream, &mut remove_actions).map_err(wrap_syntax_error)?;
112 }
113 Some(Token::Add) => {
114 if seen_add {
115 return Err("Invalid UpdateExpression: The \"ADD\" section can only be used once in an update expression;".to_string());
116 }
117 seen_add = true;
118 stream.next();
119 parse_add_clause(&mut stream, &mut add_actions).map_err(wrap_syntax_error)?;
120 }
121 Some(Token::Delete) => {
122 if seen_delete {
123 return Err("Invalid UpdateExpression: The \"DELETE\" section can only be used once in an update expression;".to_string());
124 }
125 seen_delete = true;
126 stream.next();
127 parse_delete_clause(&mut stream, &mut delete_actions).map_err(wrap_syntax_error)?;
128 }
129 Some(_) => {
130 let offending_span = stream
134 .peek_span()
135 .expect("peek_span must yield when peek did");
136 let bad = &expr[offending_span.start..offending_span.end()];
137 stream.next();
138 let next_span = stream.peek_span();
139 let near = near_window_parser(expr, offending_span, next_span);
140 return Err(format!(
141 r#"Invalid UpdateExpression: Syntax error; token: "{bad}", near: "{near}""#
142 ));
143 }
144 None => break,
145 }
146 }
147
148 Ok(UpdateExpr {
149 set_actions,
150 remove_actions,
151 add_actions,
152 delete_actions,
153 })
154}
155
156fn wrap_syntax_error(err: String) -> String {
159 if err.starts_with("Invalid UpdateExpression:") {
160 err
161 } else if err.starts_with("Attribute name is a reserved keyword") {
162 format!("Invalid UpdateExpression: {err}")
163 } else {
164 format!("Invalid UpdateExpression: Syntax error; {err}")
165 }
166}
167
168pub fn track_references(
173 expr: &UpdateExpr,
174 tracker: &TrackedExpressionAttributes,
175) -> Result<(), String> {
176 let mut all_target_paths: Vec<Vec<PathElement>> = Vec::new();
178
179 for action in &expr.set_actions {
180 track_path_refs(&action.path, tracker)?;
181 track_set_value_refs(&action.value, tracker)?;
182 all_target_paths.push(resolve_tracked_path(&action.path, tracker));
183 }
184 for path in &expr.remove_actions {
185 track_path_refs(path, tracker)?;
186 all_target_paths.push(resolve_tracked_path(path, tracker));
187 }
188 for action in &expr.add_actions {
189 track_path_refs(&action.path, tracker)?;
190 let val = tracker.resolve_value(&action.value_ref)?;
191 validate_add_type(val)?;
193 all_target_paths.push(resolve_tracked_path(&action.path, tracker));
194 }
195 for action in &expr.delete_actions {
196 track_path_refs(&action.path, tracker)?;
197 let val = tracker.resolve_value(&action.value_ref)?;
198 validate_delete_type(val)?;
200 all_target_paths.push(resolve_tracked_path(&action.path, tracker));
201 }
202
203 for action in &expr.set_actions {
205 validate_set_value_types(&action.value, tracker)?;
206 }
207
208 check_path_overlaps(&all_target_paths)?;
210
211 Ok(())
212}
213
214fn validate_add_type(val: &crate::types::AttributeValue) -> Result<(), String> {
216 use crate::types::AttributeValue;
217 match val {
218 AttributeValue::N(_)
219 | AttributeValue::SS(_)
220 | AttributeValue::NS(_)
221 | AttributeValue::BS(_) => Ok(()),
222 _ => Err(format!(
223 "Invalid UpdateExpression: Incorrect operand type for operator or function; \
224 operator: ADD, operand type: {}",
225 dynamo_type_name(val)
226 )),
227 }
228}
229
230fn validate_delete_type(val: &crate::types::AttributeValue) -> Result<(), String> {
232 use crate::types::AttributeValue;
233 match val {
234 AttributeValue::SS(_) | AttributeValue::NS(_) | AttributeValue::BS(_) => Ok(()),
235 _ => Err(format!(
236 "Invalid UpdateExpression: Incorrect operand type for operator or function; \
237 operator: DELETE, operand type: {}",
238 dynamo_type_name(val)
239 )),
240 }
241}
242
243fn dynamo_type_name(val: &crate::types::AttributeValue) -> &'static str {
245 use crate::types::AttributeValue;
246 match val {
247 AttributeValue::S(_) => "STRING",
248 AttributeValue::N(_) => "NUMBER",
249 AttributeValue::B(_) => "BINARY",
250 AttributeValue::BOOL(_) => "BOOLEAN",
251 AttributeValue::NULL(_) => "NULL",
252 AttributeValue::SS(_) => "SS",
253 AttributeValue::NS(_) => "NS",
254 AttributeValue::BS(_) => "BS",
255 AttributeValue::L(_) => "LIST",
256 AttributeValue::M(_) => "MAP",
257 }
258}
259
260fn validate_set_value_types(
262 value: &SetValue,
263 tracker: &TrackedExpressionAttributes,
264) -> Result<(), String> {
265 match value {
266 SetValue::Operand(op) => validate_set_operand_types(op, tracker),
267 SetValue::Plus(left, right) => {
268 validate_arithmetic_operand(left, "+", tracker)?;
269 validate_arithmetic_operand(right, "+", tracker)
270 }
271 SetValue::Minus(left, right) => {
272 validate_arithmetic_operand(left, "-", tracker)?;
273 validate_arithmetic_operand(right, "-", tracker)
274 }
275 }
276}
277
278fn validate_arithmetic_operand(
280 operand: &SetOperand,
281 op: &str,
282 tracker: &TrackedExpressionAttributes,
283) -> Result<(), String> {
284 use crate::types::AttributeValue;
285 match operand {
286 SetOperand::ValueRef(name) => {
287 let val = tracker.resolve_value(name)?;
288 if !matches!(val, AttributeValue::N(_)) {
289 return Err(format!(
290 "Invalid UpdateExpression: Incorrect operand type for operator or function; \
291 operator or function: {op}, operand type: {}",
292 dynamo_type_name(val)
293 ));
294 }
295 Ok(())
296 }
297 SetOperand::IfNotExists(_, default) => validate_set_operand_types(default, tracker),
298 SetOperand::ListAppend(a, b) => {
299 validate_list_append_operand(a, tracker)?;
300 validate_list_append_operand(b, tracker)
301 }
302 SetOperand::Path(_) => Ok(()), SetOperand::Group(inner) => validate_set_value_types(inner, tracker),
306 }
307}
308
309fn validate_set_operand_types(
311 operand: &SetOperand,
312 tracker: &TrackedExpressionAttributes,
313) -> Result<(), String> {
314 match operand {
315 SetOperand::ListAppend(a, b) => {
316 validate_list_append_operand(a, tracker)?;
317 validate_list_append_operand(b, tracker)
318 }
319 SetOperand::IfNotExists(_, default) => validate_set_operand_types(default, tracker),
320 SetOperand::Group(inner) => validate_set_value_types(inner, tracker),
321 _ => Ok(()),
322 }
323}
324
325fn validate_list_append_operand(
327 operand: &SetOperand,
328 tracker: &TrackedExpressionAttributes,
329) -> Result<(), String> {
330 use crate::types::AttributeValue;
331 if let SetOperand::ValueRef(name) = operand {
332 let val = tracker.resolve_value(name)?;
333 if !matches!(val, AttributeValue::L(_)) {
334 return Err(format!(
335 "Invalid UpdateExpression: Incorrect operand type for operator or function; \
336 operator or function: list_append, operand type: {}",
337 dynamo_type_name(val)
338 ));
339 }
340 }
341 Ok(())
342}
343
344fn resolve_tracked_path(
346 path: &[PathElement],
347 tracker: &TrackedExpressionAttributes,
348) -> Vec<PathElement> {
349 path.iter()
350 .map(|elem| {
351 if let PathElement::Attribute(name) = elem {
352 if name.starts_with('#') {
353 if let Ok(resolved) = tracker.resolve_name(name) {
354 return PathElement::Attribute(resolved);
355 }
356 }
357 }
358 elem.clone()
359 })
360 .collect()
361}
362
363fn check_path_overlaps(paths: &[Vec<PathElement>]) -> Result<(), String> {
369 for i in 0..paths.len() {
370 for j in (i + 1)..paths.len() {
371 let a = &paths[i];
372 let b = &paths[j];
373 let min_len = a.len().min(b.len());
374
375 let mut common = 0;
377 for k in 0..min_len {
378 if a[k] == b[k] {
379 common += 1;
380 } else {
381 break;
382 }
383 }
384
385 if common == 0 {
386 continue;
387 }
388
389 if common == a.len() || common == b.len() {
391 let (shorter, longer) = if a.len() <= b.len() { (a, b) } else { (b, a) };
392 return Err(format!(
393 "Invalid UpdateExpression: Two document paths overlap with each other; \
394 must remove or rewrite one of these paths; \
395 path one: {}, path two: {}",
396 format_path_for_error(longer),
397 format_path_for_error(shorter)
398 ));
399 }
400
401 if common > 0 && common < min_len && a == b {
403 return Err(format!(
404 "Invalid UpdateExpression: Two document paths conflict with each other; \
405 must remove or rewrite one of these paths; \
406 path one: {}, path two: {}",
407 format_path_for_error(a),
408 format_path_for_error(b)
409 ));
410 }
411 }
412 }
413 Ok(())
414}
415
416fn track_path_refs(
417 path: &[PathElement],
418 tracker: &TrackedExpressionAttributes,
419) -> Result<(), String> {
420 for elem in path {
421 if let PathElement::Attribute(name) = elem {
422 if name.starts_with('#') {
423 tracker.resolve_name(name)?;
424 }
425 }
426 }
427 Ok(())
428}
429
430fn track_set_value_refs(
431 value: &SetValue,
432 tracker: &TrackedExpressionAttributes,
433) -> Result<(), String> {
434 match value {
435 SetValue::Operand(op) => track_set_operand_refs(op, tracker),
436 SetValue::Plus(left, right) | SetValue::Minus(left, right) => {
437 track_set_operand_refs(left, tracker)?;
438 track_set_operand_refs(right, tracker)
439 }
440 }
441}
442
443fn track_set_operand_refs(
444 operand: &SetOperand,
445 tracker: &TrackedExpressionAttributes,
446) -> Result<(), String> {
447 match operand {
448 SetOperand::Path(path) => track_path_refs(path, tracker),
449 SetOperand::ValueRef(name) => {
450 tracker.resolve_value(name)?;
451 Ok(())
452 }
453 SetOperand::IfNotExists(path, default) => {
454 track_path_refs(path, tracker)?;
455 track_set_operand_refs(default, tracker)
456 }
457 SetOperand::ListAppend(a, b) => {
458 track_set_operand_refs(a, tracker)?;
459 track_set_operand_refs(b, tracker)
460 }
461 SetOperand::Group(inner) => track_set_value_refs(inner, tracker),
462 }
463}
464
465pub fn apply(
467 item: &mut HashMap<String, AttributeValue>,
468 expr: &UpdateExpr,
469 tracker: &TrackedExpressionAttributes,
470) -> Result<(), String> {
471 let snapshot = item.clone();
480 for action in &expr.set_actions {
481 let resolved_path = resolve_path_elements(&action.path, tracker)?;
482 let value = evaluate_set_value(&action.value, &snapshot, tracker)?;
483 set_path(item, &resolved_path, value)?;
484 }
485
486 for path in &expr.remove_actions {
488 let resolved_path = resolve_path_elements(path, tracker)?;
489 remove_path(item, &resolved_path)?;
490 }
491
492 for action in &expr.add_actions {
494 let resolved_path = resolve_path_elements(&action.path, tracker)?;
495 let add_val = tracker.resolve_value(&action.value_ref)?.clone();
496 apply_add(item, &resolved_path, &add_val).map_err(|_| {
497 "An operand in the update expression has an incorrect data type".to_string()
498 })?;
499 }
500
501 for action in &expr.delete_actions {
503 let resolved_path = resolve_path_elements(&action.path, tracker)?;
504 let del_val = tracker.resolve_value(&action.value_ref)?.clone();
505 apply_delete(item, &resolved_path, &del_val).map_err(|_| {
506 "An operand in the update expression has an incorrect data type".to_string()
507 })?;
508 }
509
510 Ok(())
511}
512
513fn evaluate_set_value(
518 value: &SetValue,
519 item: &HashMap<String, AttributeValue>,
520 tracker: &TrackedExpressionAttributes,
521) -> Result<AttributeValue, String> {
522 match value {
523 SetValue::Operand(op) => evaluate_set_operand(op, item, tracker),
524 SetValue::Plus(left, right) => {
525 let lv = evaluate_set_operand(left, item, tracker)?;
526 let rv = evaluate_set_operand(right, item, tracker)?;
527 match (&lv, &rv) {
528 (AttributeValue::N(a), AttributeValue::N(b)) => {
529 use bigdecimal::BigDecimal;
530 use std::str::FromStr;
531 let da = BigDecimal::from_str(a).map_err(|_| format!("Invalid number: {a}"))?;
532 let db = BigDecimal::from_str(b).map_err(|_| format!("Invalid number: {b}"))?;
533 let result = &da + &db;
534 Ok(AttributeValue::N(format_number(&result)))
535 }
536 _ => Err("Operands for + must be numbers".to_string()),
537 }
538 }
539 SetValue::Minus(left, right) => {
540 let lv = evaluate_set_operand(left, item, tracker)?;
541 let rv = evaluate_set_operand(right, item, tracker)?;
542 match (&lv, &rv) {
543 (AttributeValue::N(a), AttributeValue::N(b)) => {
544 use bigdecimal::BigDecimal;
545 use std::str::FromStr;
546 let da = BigDecimal::from_str(a).map_err(|_| format!("Invalid number: {a}"))?;
547 let db = BigDecimal::from_str(b).map_err(|_| format!("Invalid number: {b}"))?;
548 let result = &da - &db;
549 Ok(AttributeValue::N(format_number(&result)))
550 }
551 _ => Err("Operands for - must be numbers".to_string()),
552 }
553 }
554 }
555}
556
557fn evaluate_set_operand(
558 operand: &SetOperand,
559 item: &HashMap<String, AttributeValue>,
560 tracker: &TrackedExpressionAttributes,
561) -> Result<AttributeValue, String> {
562 match operand {
563 SetOperand::Path(path) => {
564 let resolved = resolve_path_elements(path, tracker)?;
565 resolve_path(item, &resolved).ok_or_else(|| {
566 "The provided expression refers to an attribute that does not exist in the item"
567 .to_string()
568 })
569 }
570 SetOperand::ValueRef(name) => Ok(tracker.resolve_value(name)?.clone()),
571 SetOperand::IfNotExists(path, default) => {
572 let resolved = resolve_path_elements(path, tracker)?;
573 match resolve_path(item, &resolved) {
574 Some(existing) => Ok(existing),
575 None => evaluate_set_operand(default, item, tracker),
576 }
577 }
578 SetOperand::ListAppend(list1, list2) => {
579 let v1 = evaluate_set_operand(list1, item, tracker)?;
580 let v2 = evaluate_set_operand(list2, item, tracker)?;
581 match (v1, v2) {
582 (AttributeValue::L(mut a), AttributeValue::L(b)) => {
583 a.extend(b);
584 Ok(AttributeValue::L(a))
585 }
586 _ => Err("list_append requires two list operands".to_string()),
587 }
588 }
589 SetOperand::Group(inner) => evaluate_set_value(inner, item, tracker),
590 }
591}
592
593pub fn apply_add_public(
599 item: &mut HashMap<String, AttributeValue>,
600 path: &[PathElement],
601 add_val: &AttributeValue,
602) -> Result<(), String> {
603 apply_add(item, path, add_val)
604}
605
606fn apply_add(
607 item: &mut HashMap<String, AttributeValue>,
608 path: &[PathElement],
609 add_val: &AttributeValue,
610) -> Result<(), String> {
611 let existing = resolve_path(item, path);
612
613 match (existing, add_val) {
614 (Some(AttributeValue::N(existing_n)), AttributeValue::N(add_n)) => {
616 use bigdecimal::BigDecimal;
617 use std::str::FromStr;
618 let de = BigDecimal::from_str(&existing_n)
619 .map_err(|_| format!("Invalid number: {existing_n}"))?;
620 let da = BigDecimal::from_str(add_n).map_err(|_| format!("Invalid number: {add_n}"))?;
621 let result = &de + &da;
622 set_path(item, path, AttributeValue::N(format_number(&result)))
623 }
624 (None, AttributeValue::N(_)) => {
625 set_path(item, path, add_val.clone())
627 }
628
629 (Some(AttributeValue::SS(mut existing_set)), AttributeValue::SS(add_set)) => {
631 for s in add_set {
632 if !existing_set.contains(s) {
633 existing_set.push(s.clone());
634 }
635 }
636 set_path(item, path, AttributeValue::SS(existing_set))
637 }
638 (None, AttributeValue::SS(_)) => set_path(item, path, add_val.clone()),
639
640 (Some(AttributeValue::NS(mut existing_set)), AttributeValue::NS(add_set)) => {
642 for n in add_set {
643 if !existing_set.contains(n) {
644 existing_set.push(n.clone());
645 }
646 }
647 set_path(item, path, AttributeValue::NS(existing_set))
648 }
649 (None, AttributeValue::NS(_)) => set_path(item, path, add_val.clone()),
650
651 (Some(AttributeValue::BS(mut existing_set)), AttributeValue::BS(add_set)) => {
653 for b in add_set {
654 if !existing_set.contains(b) {
655 existing_set.push(b.clone());
656 }
657 }
658 set_path(item, path, AttributeValue::BS(existing_set))
659 }
660 (None, AttributeValue::BS(_)) => set_path(item, path, add_val.clone()),
661
662 (Some(AttributeValue::L(mut existing_list)), AttributeValue::L(add_list)) => {
664 existing_list.extend(add_list.iter().cloned());
665 set_path(item, path, AttributeValue::L(existing_list))
666 }
667 (None, AttributeValue::L(_)) => set_path(item, path, add_val.clone()),
668
669 _ => Err("Type mismatch for attribute to update".to_string()),
670 }
671}
672
673pub fn apply_delete_public(
679 item: &mut HashMap<String, AttributeValue>,
680 path: &[PathElement],
681 del_val: &AttributeValue,
682) -> Result<(), String> {
683 apply_delete(item, path, del_val)
684}
685
686fn apply_delete(
687 item: &mut HashMap<String, AttributeValue>,
688 path: &[PathElement],
689 del_val: &AttributeValue,
690) -> Result<(), String> {
691 let existing = resolve_path(item, path);
692
693 match (existing, del_val) {
694 (Some(AttributeValue::SS(existing_set)), AttributeValue::SS(del_set)) => {
695 let new_set: Vec<String> = existing_set
696 .into_iter()
697 .filter(|s| !del_set.contains(s))
698 .collect();
699 if new_set.is_empty() {
700 remove_path(item, path)
701 } else {
702 set_path(item, path, AttributeValue::SS(new_set))
703 }
704 }
705 (Some(AttributeValue::NS(existing_set)), AttributeValue::NS(del_set)) => {
706 let new_set: Vec<String> = existing_set
707 .into_iter()
708 .filter(|n| !del_set.contains(n))
709 .collect();
710 if new_set.is_empty() {
711 remove_path(item, path)
712 } else {
713 set_path(item, path, AttributeValue::NS(new_set))
714 }
715 }
716 (Some(AttributeValue::BS(existing_set)), AttributeValue::BS(del_set)) => {
717 let new_set: Vec<Vec<u8>> = existing_set
718 .into_iter()
719 .filter(|b| !del_set.contains(b))
720 .collect();
721 if new_set.is_empty() {
722 remove_path(item, path)
723 } else {
724 set_path(item, path, AttributeValue::BS(new_set))
725 }
726 }
727 (None, _) => Ok(()), _ => Err("Type mismatch for attribute to update".to_string()),
729 }
730}
731
732fn parse_set_clause(stream: &mut TokenStream, actions: &mut Vec<SetAction>) -> Result<(), String> {
737 actions.push(parse_set_action(stream)?);
738 while matches!(stream.peek(), Some(Token::Comma)) {
739 stream.next();
740 actions.push(parse_set_action(stream)?);
741 }
742 Ok(())
743}
744
745fn parse_set_action(stream: &mut TokenStream) -> Result<SetAction, String> {
746 let path = parse_raw_path(stream)?;
747 stream.expect(&Token::Eq)?;
748 let value = parse_set_value(stream)?;
749 Ok(SetAction { path, value })
750}
751
752fn parse_set_value(stream: &mut TokenStream) -> Result<SetValue, String> {
753 let left = parse_set_operand(stream)?;
754
755 match stream.peek() {
756 Some(Token::Plus) => {
757 stream.next();
758 let right = parse_set_operand(stream)?;
759 Ok(SetValue::Plus(left, right))
760 }
761 Some(Token::Minus) => {
762 stream.next();
763 let right = parse_set_operand(stream)?;
764 Ok(SetValue::Minus(left, right))
765 }
766 _ => Ok(SetValue::Operand(left)),
767 }
768}
769
770fn parse_set_operand(stream: &mut TokenStream) -> Result<SetOperand, String> {
771 if let Some(Token::Identifier(name)) = stream.peek() {
773 let func_name = name.to_lowercase();
774 let orig_name = name.clone();
775 match func_name.as_str() {
776 "if_not_exists" => {
777 stream.next();
778 stream.expect(&Token::LParen)?;
779
780 match stream.peek() {
782 Some(Token::ValueRef(_)) => {
783 return Err(
784 "Invalid UpdateExpression: Operator or function requires a document path; \
785 operator or function: if_not_exists".to_string()
786 );
787 }
788 Some(Token::Identifier(fname))
789 if fname.to_lowercase() == "if_not_exists"
790 || fname.to_lowercase() == "list_append" =>
791 {
792 return Err(
793 "Invalid UpdateExpression: Operator or function requires a document path; \
794 operator or function: if_not_exists".to_string()
795 );
796 }
797 _ => {}
798 }
799
800 let path = parse_raw_path(stream)?;
801
802 if !matches!(stream.peek(), Some(Token::Comma)) {
804 return Err(
805 "Invalid UpdateExpression: Incorrect number of operands for operator or function; \
806 operator or function: if_not_exists, number of operands: 1".to_string()
807 );
808 }
809 stream.expect(&Token::Comma)?;
810 let default = parse_set_operand(stream)?;
811 stream.expect(&Token::RParen)?;
812 return Ok(SetOperand::IfNotExists(path, Box::new(default)));
813 }
814 "list_append" => {
815 stream.next();
816 stream.expect(&Token::LParen)?;
817 let list1 = parse_set_operand(stream)?;
818
819 if !matches!(stream.peek(), Some(Token::Comma)) {
821 return Err(
822 "Invalid UpdateExpression: Incorrect number of operands for operator or function; \
823 operator or function: list_append, number of operands: 1".to_string()
824 );
825 }
826 stream.expect(&Token::Comma)?;
827 let list2 = parse_set_operand(stream)?;
828 stream.expect(&Token::RParen)?;
829 return Ok(SetOperand::ListAppend(Box::new(list1), Box::new(list2)));
830 }
831 _ => {
832 let saved_pos = stream.pos();
835 stream.next();
836 if matches!(stream.peek(), Some(Token::LParen)) {
837 return Err(format!(
838 "Invalid UpdateExpression: Invalid function name; function: {}",
839 orig_name
840 ));
841 }
842 stream.set_pos(saved_pos);
844 }
845 }
846 }
847
848 match stream.peek() {
849 Some(Token::LParen) => {
852 stream.next();
853 let inner = parse_set_value(stream)?;
854 stream.expect(&Token::RParen)?;
855 Ok(SetOperand::Group(Box::new(inner)))
856 }
857 Some(Token::ValueRef(_)) => {
858 if let Some(Token::ValueRef(name)) = stream.next().cloned() {
859 Ok(SetOperand::ValueRef(name))
860 } else {
861 unreachable!()
862 }
863 }
864 Some(Token::Identifier(_)) | Some(Token::NameRef(_)) => {
865 let path = parse_raw_path(stream)?;
866 Ok(SetOperand::Path(path))
867 }
868 Some(t) => Err(format!("Expected operand in SET, got {t}")),
869 None => Err("Expected operand in SET, got end of expression".to_string()),
870 }
871}
872
873fn parse_remove_clause(
874 stream: &mut TokenStream,
875 actions: &mut Vec<Vec<PathElement>>,
876) -> Result<(), String> {
877 actions.push(parse_raw_path(stream)?);
878 while matches!(stream.peek(), Some(Token::Comma)) {
879 stream.next();
880 actions.push(parse_raw_path(stream)?);
881 }
882 Ok(())
883}
884
885fn parse_add_clause(stream: &mut TokenStream, actions: &mut Vec<AddAction>) -> Result<(), String> {
886 actions.push(parse_add_action(stream)?);
887 while matches!(stream.peek(), Some(Token::Comma)) {
888 stream.next();
889 actions.push(parse_add_action(stream)?);
890 }
891 Ok(())
892}
893
894fn parse_add_action(stream: &mut TokenStream) -> Result<AddAction, String> {
895 let path = parse_raw_path(stream)?;
896 match stream.next() {
897 Some(Token::ValueRef(name)) => Ok(AddAction {
898 path,
899 value_ref: name.clone(),
900 }),
901 Some(t) => Err(format!("Expected value reference in ADD, got {t}")),
902 None => Err("Expected value reference in ADD, got end of expression".to_string()),
903 }
904}
905
906fn parse_delete_clause(
907 stream: &mut TokenStream,
908 actions: &mut Vec<DeleteAction>,
909) -> Result<(), String> {
910 actions.push(parse_delete_action(stream)?);
911 while matches!(stream.peek(), Some(Token::Comma)) {
912 stream.next();
913 actions.push(parse_delete_action(stream)?);
914 }
915 Ok(())
916}
917
918fn parse_delete_action(stream: &mut TokenStream) -> Result<DeleteAction, String> {
919 let path = parse_raw_path(stream)?;
920 match stream.next() {
921 Some(Token::ValueRef(name)) => Ok(DeleteAction {
922 path,
923 value_ref: name.clone(),
924 }),
925 Some(t) => Err(format!("Expected value reference in DELETE, got {t}")),
926 None => Err("Expected value reference in DELETE, got end of expression".to_string()),
927 }
928}
929
930fn format_number(n: &bigdecimal::BigDecimal) -> String {
933 let normalized = n.normalized();
934 if normalized.as_bigint_and_exponent().1 < 0 {
938 normalized.with_scale(0).to_string()
939 } else {
940 normalized.to_string()
941 }
942}
943
944#[cfg(test)]
945mod tests {
946 use super::*;
947
948 fn make_item(pairs: &[(&str, AttributeValue)]) -> HashMap<String, AttributeValue> {
949 pairs
950 .iter()
951 .map(|(k, v)| (k.to_string(), v.clone()))
952 .collect()
953 }
954
955 fn vals(pairs: &[(&str, AttributeValue)]) -> Option<HashMap<String, AttributeValue>> {
956 Some(make_item(pairs))
957 }
958
959 fn make_tracker<'a>(
960 names: &'a Option<HashMap<String, String>>,
961 values: &'a Option<HashMap<String, AttributeValue>>,
962 ) -> TrackedExpressionAttributes<'a> {
963 TrackedExpressionAttributes::new(names, values)
964 }
965
966 #[test]
967 fn test_set_simple() {
968 let expr = parse("SET label = :val").unwrap();
969 assert_eq!(expr.set_actions.len(), 1);
970 assert!(expr.remove_actions.is_empty());
971 }
972
973 #[test]
974 fn test_set_multiple() {
975 let expr = parse("SET a = :v1, b = :v2").unwrap();
976 assert_eq!(expr.set_actions.len(), 2);
977 }
978
979 #[test]
980 fn test_set_arithmetic_plus() {
981 let expr = parse("SET tally = tally + :inc").unwrap();
982 let mut item = make_item(&[
983 ("pk", AttributeValue::S("k".into())),
984 ("tally", AttributeValue::N("10".into())),
985 ]);
986 let av = vals(&[(":inc", AttributeValue::N("5".into()))]);
987 let no_names = None;
988 let tracker = make_tracker(&no_names, &av);
989 apply(&mut item, &expr, &tracker).unwrap();
990 assert_eq!(item["tally"], AttributeValue::N("15".into()));
991 }
992
993 #[test]
994 fn test_set_arithmetic_minus() {
995 let expr = parse("SET price = price - :discount").unwrap();
996 let mut item = make_item(&[
997 ("pk", AttributeValue::S("k".into())),
998 ("price", AttributeValue::N("100".into())),
999 ]);
1000 let av = vals(&[(":discount", AttributeValue::N("25".into()))]);
1001 let no_names = None;
1002 let tracker = make_tracker(&no_names, &av);
1003 apply(&mut item, &expr, &tracker).unwrap();
1004 assert_eq!(item["price"], AttributeValue::N("75".into()));
1005 }
1006
1007 #[test]
1008 fn test_set_if_not_exists() {
1009 let expr = parse("SET hits = if_not_exists(hits, :zero)").unwrap();
1010 let mut item = make_item(&[("pk", AttributeValue::S("k".into()))]);
1011 let av = vals(&[(":zero", AttributeValue::N("0".into()))]);
1012 let no_names = None;
1013 let tracker = make_tracker(&no_names, &av);
1014 apply(&mut item, &expr, &tracker).unwrap();
1015 assert_eq!(item["hits"], AttributeValue::N("0".into()));
1016
1017 let tracker2 = make_tracker(&no_names, &av);
1019 apply(&mut item, &expr, &tracker2).unwrap();
1020 assert_eq!(item["hits"], AttributeValue::N("0".into()));
1021 }
1022
1023 #[test]
1024 fn test_set_list_append() {
1025 let expr = parse("SET entries = list_append(entries, :new)").unwrap();
1026 let mut item = make_item(&[
1027 ("pk", AttributeValue::S("k".into())),
1028 (
1029 "entries",
1030 AttributeValue::L(vec![AttributeValue::S("a".into())]),
1031 ),
1032 ]);
1033 let av = vals(&[(
1034 ":new",
1035 AttributeValue::L(vec![AttributeValue::S("b".into())]),
1036 )]);
1037 let no_names = None;
1038 let tracker = make_tracker(&no_names, &av);
1039 apply(&mut item, &expr, &tracker).unwrap();
1040 if let AttributeValue::L(list) = &item["entries"] {
1041 assert_eq!(list.len(), 2);
1042 } else {
1043 panic!("Expected list");
1044 }
1045 }
1046
1047 #[test]
1048 fn test_remove() {
1049 let expr = parse("REMOVE attr1, attr2").unwrap();
1050 let mut item = make_item(&[
1051 ("pk", AttributeValue::S("k".into())),
1052 ("attr1", AttributeValue::S("a".into())),
1053 ("attr2", AttributeValue::S("b".into())),
1054 ("attr3", AttributeValue::S("c".into())),
1055 ]);
1056 let no_names = None;
1057 let no_values = None;
1058 let tracker = make_tracker(&no_names, &no_values);
1059 apply(&mut item, &expr, &tracker).unwrap();
1060 assert!(!item.contains_key("attr1"));
1061 assert!(!item.contains_key("attr2"));
1062 assert!(item.contains_key("attr3"));
1063 }
1064
1065 #[test]
1066 fn test_add_number() {
1067 let expr = parse("ADD tally :inc").unwrap();
1068 let mut item = make_item(&[
1069 ("pk", AttributeValue::S("k".into())),
1070 ("tally", AttributeValue::N("10".into())),
1071 ]);
1072 let av = vals(&[(":inc", AttributeValue::N("5".into()))]);
1073 let no_names = None;
1074 let tracker = make_tracker(&no_names, &av);
1075 apply(&mut item, &expr, &tracker).unwrap();
1076 assert_eq!(item["tally"], AttributeValue::N("15".into()));
1077 }
1078
1079 #[test]
1080 fn test_add_number_create() {
1081 let expr = parse("ADD tally :val").unwrap();
1082 let mut item = make_item(&[("pk", AttributeValue::S("k".into()))]);
1083 let av = vals(&[(":val", AttributeValue::N("1".into()))]);
1084 let no_names = None;
1085 let tracker = make_tracker(&no_names, &av);
1086 apply(&mut item, &expr, &tracker).unwrap();
1087 assert_eq!(item["tally"], AttributeValue::N("1".into()));
1088 }
1089
1090 #[test]
1091 fn test_add_string_set() {
1092 let expr = parse("ADD colors :new_colors").unwrap();
1093 let mut item = make_item(&[
1094 ("pk", AttributeValue::S("k".into())),
1095 (
1096 "colors",
1097 AttributeValue::SS(vec!["red".into(), "blue".into()]),
1098 ),
1099 ]);
1100 let av = vals(&[(
1101 ":new_colors",
1102 AttributeValue::SS(vec!["blue".into(), "green".into()]),
1103 )]);
1104 let no_names = None;
1105 let tracker = make_tracker(&no_names, &av);
1106 apply(&mut item, &expr, &tracker).unwrap();
1107 if let AttributeValue::SS(set) = &item["colors"] {
1108 assert_eq!(set.len(), 3); assert!(set.contains(&"green".to_string()));
1110 } else {
1111 panic!("Expected SS");
1112 }
1113 }
1114
1115 #[test]
1116 fn test_delete_string_set() {
1117 let expr = parse("DELETE colors :remove").unwrap();
1118 let mut item = make_item(&[
1119 ("pk", AttributeValue::S("k".into())),
1120 (
1121 "colors",
1122 AttributeValue::SS(vec!["red".into(), "blue".into(), "green".into()]),
1123 ),
1124 ]);
1125 let av = vals(&[(
1126 ":remove",
1127 AttributeValue::SS(vec!["blue".into(), "green".into()]),
1128 )]);
1129 let no_names = None;
1130 let tracker = make_tracker(&no_names, &av);
1131 apply(&mut item, &expr, &tracker).unwrap();
1132 if let AttributeValue::SS(set) = &item["colors"] {
1133 assert_eq!(set, &vec!["red".to_string()]);
1134 } else {
1135 panic!("Expected SS");
1136 }
1137 }
1138
1139 #[test]
1140 fn test_combined_set_remove() {
1141 let expr = parse("SET label = :name REMOVE old_attr").unwrap();
1142 assert_eq!(expr.set_actions.len(), 1);
1143 assert_eq!(expr.remove_actions.len(), 1);
1144 }
1145
1146 #[test]
1147 fn test_duplicate_clause_error() {
1148 let result = parse("SET a = :v SET b = :w");
1149 assert!(result.is_err());
1150 assert!(result.unwrap_err().contains("only be used once"));
1151 }
1152
1153 #[test]
1155 fn test_set_reads_pre_update_snapshot() {
1156 let expr = parse("SET a = :v, b = a").unwrap();
1157 let mut item = make_item(&[
1158 ("pk", AttributeValue::S("k".into())),
1159 ("a", AttributeValue::S("OLD".into())),
1160 ]);
1161 let av = vals(&[(":v", AttributeValue::S("NEW".into()))]);
1162 let no_names = None;
1163 let tracker = make_tracker(&no_names, &av);
1164 apply(&mut item, &expr, &tracker).unwrap();
1165 assert_eq!(item["a"], AttributeValue::S("NEW".into()));
1166 assert_eq!(item["b"], AttributeValue::S("OLD".into()));
1167 }
1168
1169 #[test]
1171 fn test_set_parenthesised_arithmetic() {
1172 let expr = parse("SET c = (c - :v)").unwrap();
1173 let mut item = make_item(&[
1174 ("pk", AttributeValue::S("k".into())),
1175 ("c", AttributeValue::N("10".into())),
1176 ]);
1177 let av = vals(&[(":v", AttributeValue::N("3".into()))]);
1178 let no_names = None;
1179 let tracker = make_tracker(&no_names, &av);
1180 apply(&mut item, &expr, &tracker).unwrap();
1181 assert_eq!(item["c"], AttributeValue::N("7".into()));
1182 }
1183
1184 #[test]
1186 fn test_set_parenthesised_arithmetic_bigdecimal() {
1187 let expr = parse("SET c = (c + :v)").unwrap();
1188 let mut item = make_item(&[
1189 ("pk", AttributeValue::S("k".into())),
1190 ("c", AttributeValue::N("100000000000000000000".into())),
1191 ]);
1192 let av = vals(&[(":v", AttributeValue::N("1".into()))]);
1193 let no_names = None;
1194 let tracker = make_tracker(&no_names, &av);
1195 apply(&mut item, &expr, &tracker).unwrap();
1196 assert_eq!(item["c"], AttributeValue::N("100000000000000000001".into()));
1197 }
1198}