1use crate::types::AttributeValue;
10use std::collections::HashMap;
11
12#[derive(Debug, Clone)]
14pub enum Statement {
15 Select {
16 table_name: String,
17 projections: Vec<String>, where_clause: Option<WhereClause>,
19 },
20 Insert {
21 table_name: String,
22 item: HashMap<String, PartiqlValue>,
23 if_not_exists: bool,
24 },
25 Update {
26 table_name: String,
27 set_clauses: Vec<SetClause>,
28 remove_paths: Vec<String>,
29 where_clause: Option<WhereClause>,
30 },
31 Delete {
32 table_name: String,
33 where_clause: Option<WhereClause>,
34 },
35}
36
37pub fn table_name(stmt: &Statement) -> Option<&str> {
39 match stmt {
40 Statement::Select { table_name, .. }
41 | Statement::Insert { table_name, .. }
42 | Statement::Update { table_name, .. }
43 | Statement::Delete { table_name, .. } => Some(table_name),
44 }
45}
46
47#[derive(Debug, Clone)]
49pub struct SetClause {
50 pub path: String,
51 pub value: SetValue,
52}
53
54#[derive(Debug, Clone, PartialEq)]
57pub enum SetValue {
58 Simple(PartiqlValue),
60 Add(String, PartiqlValue),
62 Sub(String, PartiqlValue),
64 ListAppend(PartiqlValue, PartiqlValue),
66}
67
68#[derive(Debug, Clone)]
73pub struct WhereClause {
74 pub groups: Vec<Vec<WhereCondition>>,
76}
77
78impl WhereClause {
79 pub fn from_conditions(conditions: Vec<WhereCondition>) -> Self {
81 Self {
82 groups: vec![conditions],
83 }
84 }
85
86 pub fn from_groups(groups: Vec<Vec<WhereCondition>>) -> Self {
88 Self { groups }
89 }
90}
91
92#[derive(Debug, Clone)]
94pub enum WhereCondition {
95 Comparison(Condition),
96 Exists(String),
97 NotExists(String),
98 BeginsWith(String, PartiqlValue),
99 NotBeginsWith(String, PartiqlValue),
100 Between(String, PartiqlValue, PartiqlValue),
101 In(String, Vec<PartiqlValue>),
102 Contains(String, PartiqlValue),
103 IsMissing(String),
104 IsNotMissing(String),
105}
106
107#[derive(Debug, Clone)]
109pub struct Condition {
110 pub path: String,
111 pub op: CompOp,
112 pub value: PartiqlValue,
113}
114
115#[derive(Debug, Clone, PartialEq)]
117pub enum CompOp {
118 Eq,
119 Ne,
120 Lt,
121 Le,
122 Gt,
123 Ge,
124}
125
126#[derive(Debug, Clone, PartialEq)]
128pub enum PartiqlValue {
129 Literal(AttributeValue),
130 Parameter(usize), }
132
133pub fn parse(input: &str) -> Result<Statement, String> {
135 let mut tokenizer = Tokenizer::new(input)?;
136 let first = tokenizer
137 .next_token()?
138 .ok_or("Empty statement")?
139 .to_uppercase();
140
141 match first.as_str() {
142 "SELECT" => parse_select(&mut tokenizer),
143 "INSERT" => parse_insert(&mut tokenizer),
144 "UPDATE" => parse_update(&mut tokenizer),
145 "DELETE" => parse_delete(&mut tokenizer),
146 other => Err(format!("Unsupported statement type: {other}")),
147 }
148}
149
150fn parse_select(t: &mut Tokenizer) -> Result<Statement, String> {
151 let projections = parse_projections(t)?;
153
154 expect_keyword(t, "FROM")?;
156
157 let table_name = parse_table_name(t)?;
159
160 let where_clause = parse_optional_where(t)?;
162
163 Ok(Statement::Select {
164 table_name,
165 projections,
166 where_clause,
167 })
168}
169
170fn parse_projections(t: &mut Tokenizer) -> Result<Vec<String>, String> {
171 let tok = t.peek_token()?.ok_or("Expected projection")?;
172
173 if tok == "*" {
174 t.next_token()?; return Ok(Vec::new());
176 }
177
178 if tok.eq_ignore_ascii_case("COUNT") {
180 t.next_token()?; expect_char(t, "(")?;
182 expect_char(t, "*")?;
183 expect_char(t, ")")?;
184 return Ok(vec!["COUNT(*)".to_string()]);
185 }
186
187 let mut projections = Vec::new();
188 loop {
189 let name = t
190 .next_token()?
191 .ok_or("Expected projection attribute name")?;
192 let mut path = unquote(&name);
193
194 loop {
196 match t.peek_token()? {
197 Some(ref s) if s == "." => {
198 t.next_token()?; let segment = t.next_token()?.ok_or("Expected attribute name after '.'")?;
200 path.push('.');
201 path.push_str(&unquote(&segment));
202 }
203 Some(ref s) if s == "[" => {
204 t.next_token()?; let idx = t.next_token()?.ok_or("Expected index in '[]'")?;
206 let close = t.next_token()?.ok_or("Expected ']'")?;
207 if close != "]" {
208 return Err(format!("Expected ']' but got '{close}'"));
209 }
210 path.push('[');
211 path.push_str(&idx);
212 path.push(']');
213 }
214 _ => break,
215 }
216 }
217
218 projections.push(path);
219
220 match t.peek_token()? {
221 Some(ref s) if s == "," => {
222 t.next_token()?; }
224 _ => break,
225 }
226 }
227
228 Ok(projections)
229}
230
231fn parse_insert(t: &mut Tokenizer) -> Result<Statement, String> {
232 expect_keyword(t, "INTO")?;
233 let table_name = parse_table_name(t)?;
234 expect_keyword(t, "VALUE")?;
235
236 let item = parse_item_literal_partiql(t)?;
238
239 let if_not_exists = if let Some(ref tok) = t.peek_token()? {
241 if tok.eq_ignore_ascii_case("IF") {
242 t.next_token()?; expect_keyword(t, "NOT")?;
244 expect_keyword(t, "EXISTS")?;
245 true
246 } else {
247 false
248 }
249 } else {
250 false
251 };
252
253 Ok(Statement::Insert {
254 table_name,
255 item,
256 if_not_exists,
257 })
258}
259
260fn parse_update(t: &mut Tokenizer) -> Result<Statement, String> {
261 let table_name = parse_table_name(t)?;
262
263 let mut set_clauses = Vec::new();
266 let mut remove_paths = Vec::new();
267
268 if let Some(ref tok) = t.peek_token()? {
269 if tok.eq_ignore_ascii_case("SET") {
270 t.next_token()?; loop {
272 let path_tok = t.next_token()?.ok_or("Expected attribute path in SET")?;
273 let path = parse_dotted_path_from_token(&path_tok, t)?;
274
275 let eq = t.next_token()?.ok_or("Expected '='")?;
276 if eq != "=" {
277 return Err(format!("Expected '=' but got '{eq}'"));
278 }
279
280 let value = parse_set_value(t)?;
281 set_clauses.push(SetClause { path, value });
282
283 match t.peek_token()? {
284 Some(ref s) if s == "," => {
285 t.next_token()?; }
287 _ => break,
288 }
289 }
290 }
291 }
292
293 if let Some(ref tok) = t.peek_token()? {
295 if tok.eq_ignore_ascii_case("REMOVE") {
296 t.next_token()?; loop {
298 let path_tok = t.next_token()?.ok_or("Expected attribute path in REMOVE")?;
299 let path = parse_dotted_path_from_token(&path_tok, t)?;
300 remove_paths.push(path);
301 match t.peek_token()? {
302 Some(ref s) if s == "," => {
303 t.next_token()?;
304 }
305 _ => break,
306 }
307 }
308 }
309 }
310
311 if set_clauses.is_empty() && remove_paths.is_empty() {
312 return Err("UPDATE requires at least one SET or REMOVE clause".to_string());
313 }
314
315 let where_clause = parse_optional_where(t)?;
316
317 Ok(Statement::Update {
318 table_name,
319 set_clauses,
320 remove_paths,
321 where_clause,
322 })
323}
324
325fn parse_set_value(t: &mut Tokenizer) -> Result<SetValue, String> {
328 if let Some(ref tok) = t.peek_token()? {
330 if tok.eq_ignore_ascii_case("list_append") {
331 t.next_token()?; expect_char(t, "(")?;
333 let first = parse_value(t)?;
334 let comma = t.next_token()?.ok_or("Expected ',' in list_append")?;
335 if comma != "," {
336 return Err(format!("Expected ',' but got '{comma}'"));
337 }
338 let second = parse_value(t)?;
339 expect_char(t, ")")?;
340 return Ok(SetValue::ListAppend(first, second));
341 }
342 }
343
344 let first = parse_value(t)?;
345
346 match t.peek_token()? {
348 Some(ref s) if s == "+" => {
349 t.next_token()?; let second = parse_value(t)?;
351 let attr_path = match &first {
354 PartiqlValue::Literal(AttributeValue::S(s)) => s.clone(),
355 _ => {
360 return Err(
361 "Expected attribute path on left side of '+' expression".to_string()
362 );
363 }
364 };
365 Ok(SetValue::Add(attr_path, second))
366 }
367 Some(ref s) if s == "-" => {
368 t.next_token()?; let second = parse_value(t)?;
370 let attr_path = match &first {
371 PartiqlValue::Literal(AttributeValue::S(s)) => s.clone(),
372 _ => {
373 return Err(
374 "Expected attribute path on left side of '-' expression".to_string()
375 );
376 }
377 };
378 Ok(SetValue::Sub(attr_path, second))
379 }
380 _ => Ok(SetValue::Simple(first)),
381 }
382}
383
384fn parse_delete(t: &mut Tokenizer) -> Result<Statement, String> {
385 expect_keyword(t, "FROM")?;
386 let table_name = parse_table_name(t)?;
387 let where_clause = parse_optional_where(t)?;
388
389 Ok(Statement::Delete {
390 table_name,
391 where_clause,
392 })
393}
394
395fn parse_table_name(t: &mut Tokenizer) -> Result<String, String> {
396 let name = t.next_token()?.ok_or("Expected table name")?;
397 Ok(unquote(&name))
398}
399
400fn parse_optional_where(t: &mut Tokenizer) -> Result<Option<WhereClause>, String> {
401 match t.peek_token()? {
402 Some(ref s) if s.eq_ignore_ascii_case("WHERE") => {
403 t.next_token()?; let groups = parse_conditions_with_or(t)?;
405 Ok(Some(WhereClause::from_groups(groups)))
406 }
407 _ => Ok(None),
408 }
409}
410
411fn parse_conditions_with_or(t: &mut Tokenizer) -> Result<Vec<Vec<WhereCondition>>, String> {
414 let mut groups: Vec<Vec<WhereCondition>> = Vec::new();
415 let mut current_group: Vec<WhereCondition> = Vec::new();
416
417 loop {
418 let condition = parse_single_condition(t)?;
419 current_group.push(condition);
420
421 match t.peek_token()? {
422 Some(ref s) if s.eq_ignore_ascii_case("AND") => {
423 t.next_token()?; }
425 Some(ref s) if s.eq_ignore_ascii_case("OR") => {
426 t.next_token()?; groups.push(current_group);
428 current_group = Vec::new();
429 }
430 _ => break,
431 }
432 }
433
434 groups.push(current_group);
435 Ok(groups)
436}
437
438fn parse_single_condition(t: &mut Tokenizer) -> Result<WhereCondition, String> {
440 let tok = t.next_token()?.ok_or("Expected condition in WHERE")?;
441 let tok_upper = tok.to_uppercase();
442
443 match tok_upper.as_str() {
444 "EXISTS" => {
445 expect_char(t, "(")?;
446 let path = parse_function_path(t)?;
447 expect_char(t, ")")?;
448 Ok(WhereCondition::Exists(path))
449 }
450 "BEGINS_WITH" => {
451 expect_char(t, "(")?;
452 let path = parse_function_path(t)?;
453 let comma = t.next_token()?.ok_or("Expected ',' in BEGINS_WITH")?;
454 if comma != "," {
455 return Err(format!("Expected ',' but got '{comma}'"));
456 }
457 let value = parse_value(t)?;
458 expect_char(t, ")")?;
459 Ok(WhereCondition::BeginsWith(path, value))
460 }
461 "CONTAINS" => {
462 expect_char(t, "(")?;
463 let path = parse_function_path(t)?;
464 let comma = t.next_token()?.ok_or("Expected ',' in CONTAINS")?;
465 if comma != "," {
466 return Err(format!("Expected ',' but got '{comma}'"));
467 }
468 let value = parse_value(t)?;
469 expect_char(t, ")")?;
470 Ok(WhereCondition::Contains(path, value))
471 }
472 "NOT" => {
473 let func = t.next_token()?.ok_or("Expected function name after NOT")?;
474 if func.eq_ignore_ascii_case("EXISTS") {
475 expect_char(t, "(")?;
476 let path = parse_function_path(t)?;
477 expect_char(t, ")")?;
478 Ok(WhereCondition::NotExists(path))
479 } else if func.eq_ignore_ascii_case("BEGINS_WITH") {
480 expect_char(t, "(")?;
481 let path = parse_function_path(t)?;
482 let comma = t.next_token()?.ok_or("Expected ',' in NOT BEGINS_WITH")?;
483 if comma != "," {
484 return Err(format!("Expected ',' but got '{comma}'"));
485 }
486 let value = parse_value(t)?;
487 expect_char(t, ")")?;
488 Ok(WhereCondition::NotBeginsWith(path, value))
489 } else {
490 Err(format!("Unsupported NOT function: {func}"))
491 }
492 }
493 _ => {
494 let path = parse_dotted_path_from_token(&tok, t)?;
497
498 let next = t
500 .peek_token()?
501 .ok_or("Expected operator after attribute path")?;
502 let next_upper = next.to_uppercase();
503
504 match next_upper.as_str() {
505 "BETWEEN" => {
506 t.next_token()?; let low = parse_value(t)?;
508 expect_keyword(t, "AND")?;
509 let high = parse_value(t)?;
510 Ok(WhereCondition::Between(path, low, high))
511 }
512 "IN" => {
513 t.next_token()?; let open = t.next_token()?.ok_or("Expected '(' or '[' after IN")?;
518 let close_char = match open.as_str() {
519 "(" => ")",
520 "[" => "]",
521 other => {
522 return Err(format!("Expected '(' or '[' after IN, got '{other}'"));
523 }
524 };
525 let mut values = Vec::new();
526 loop {
527 let peek = t.peek_token()?.ok_or("Unexpected end of IN list")?;
528 if peek == close_char {
529 t.next_token()?; break;
531 }
532 if peek == "," {
533 t.next_token()?; continue;
535 }
536 values.push(parse_value(t)?);
537 }
538 Ok(WhereCondition::In(path, values))
539 }
540 "IS" => {
541 t.next_token()?; let kw = t.next_token()?.ok_or("Expected MISSING or NOT after IS")?;
543 let kw_upper = kw.to_uppercase();
544 match kw_upper.as_str() {
545 "MISSING" => Ok(WhereCondition::IsMissing(path)),
546 "NOT" => {
547 expect_keyword(t, "MISSING")?;
548 Ok(WhereCondition::IsNotMissing(path))
549 }
550 other => Err(format!(
551 "Expected MISSING or NOT MISSING after IS, got '{other}'"
552 )),
553 }
554 }
555 _ => {
556 let op_tok = t.next_token()?.ok_or("Expected comparison operator")?;
558 let op = match op_tok.as_str() {
559 "=" => CompOp::Eq,
560 "<>" | "!=" => CompOp::Ne,
561 "<" => CompOp::Lt,
562 "<=" => CompOp::Le,
563 ">" => CompOp::Gt,
564 ">=" => CompOp::Ge,
565 other => return Err(format!("Unknown operator: {other}")),
566 };
567 let value = parse_value(t)?;
568 Ok(WhereCondition::Comparison(Condition { path, op, value }))
569 }
570 }
571 }
572 }
573}
574
575fn parse_dotted_path_from_token(first_tok: &str, t: &mut Tokenizer) -> Result<String, String> {
578 let mut path = unquote(first_tok);
579 while let Some(ref next) = t.peek_token()? {
580 if next == "." {
581 t.next_token()?; let seg = t.next_token()?.ok_or("Expected attribute name after '.'")?;
583 path.push('.');
584 path.push_str(&unquote(&seg));
585 } else if next == "[" {
586 t.next_token()?; let idx = t.next_token()?.ok_or("Expected index in '[]'")?;
588 let close = t.next_token()?.ok_or("Expected ']'")?;
589 if close != "]" {
590 return Err(format!("Expected ']' but got '{close}'"));
591 }
592 path.push('[');
593 path.push_str(&idx);
594 path.push(']');
595 } else {
596 break;
597 }
598 }
599 Ok(path)
600}
601
602fn parse_function_path(t: &mut Tokenizer) -> Result<String, String> {
605 let tok = t.next_token()?.ok_or("Expected path in function")?;
606 parse_dotted_path_from_token(&tok, t)
607}
608
609fn expect_char(t: &mut Tokenizer, expected: &str) -> Result<(), String> {
610 let tok = t.next_token()?.ok_or(format!("Expected '{expected}'"))?;
611 if tok != expected {
612 return Err(format!("Expected '{expected}' but got '{tok}'"));
613 }
614 Ok(())
615}
616
617fn parse_value(t: &mut Tokenizer) -> Result<PartiqlValue, String> {
618 let tok = t.next_token()?.ok_or("Expected value")?;
619
620 if tok == "?" {
621 let idx = t.next_param_index();
622 return Ok(PartiqlValue::Parameter(idx));
623 }
624
625 if tok.starts_with('\'') && tok.ends_with('\'') && tok.len() >= 2 {
627 let s = tok[1..tok.len() - 1].to_string();
628 return Ok(PartiqlValue::Literal(AttributeValue::S(s)));
629 }
630
631 if tok == "<" {
633 if let Some(ref next) = t.peek_token()? {
634 if next == "<" {
635 t.next_token()?; let mut elements = Vec::new();
637 loop {
638 let peek = t.peek_token()?.ok_or("Unexpected end of set literal")?;
639 if peek == ">" {
640 t.next_token()?; let next_close = t.peek_token()?;
643 if next_close.as_deref() == Some(">") {
644 t.next_token()?;
645 }
646 break;
647 }
648 if peek == "," {
649 t.next_token()?;
650 continue;
651 }
652 elements.push(parse_value(t)?);
653 }
654 return set_literal_to_value(elements);
655 }
656 }
657 }
658
659 if tok == "[" {
661 let mut items = Vec::new();
662 loop {
663 let peek = t.peek_token()?.ok_or("Unexpected end of list")?;
664 if peek == "]" {
665 t.next_token()?;
666 break;
667 }
668 if peek == "," {
669 t.next_token()?;
670 continue;
671 }
672 items.push(parse_value(t)?);
673 }
674 let mut avs = Vec::new();
676 for item in items {
677 match item {
678 PartiqlValue::Literal(av) => avs.push(av),
679 PartiqlValue::Parameter(_) => {
680 return Err(
684 "Parameter placeholders inside list literals are not yet supported"
685 .to_string(),
686 );
687 }
688 }
689 }
690 return Ok(PartiqlValue::Literal(AttributeValue::L(avs)));
691 }
692
693 if tok == "{" {
695 let mut map = HashMap::new();
696 loop {
697 let peek = t.peek_token()?.ok_or("Unexpected end of map literal")?;
698 if peek == "}" {
699 t.next_token()?;
700 break;
701 }
702 if peek == "," {
703 t.next_token()?;
704 continue;
705 }
706 let key_tok = t.next_token()?.ok_or("Expected key in map literal")?;
707 let key = unquote(&key_tok);
708 let colon = t.next_token()?.ok_or("Expected ':'")?;
709 if colon != ":" {
710 return Err(format!("Expected ':' but got '{colon}'"));
711 }
712 let val = parse_value(t)?;
713 match val {
714 PartiqlValue::Literal(av) => {
715 map.insert(key, av);
716 }
717 PartiqlValue::Parameter(_) => {
718 return Err(
719 "Parameter placeholders inside map literals are not yet supported"
720 .to_string(),
721 );
722 }
723 }
724 }
725 return Ok(PartiqlValue::Literal(AttributeValue::M(map)));
726 }
727
728 if tok == "-" || tok == "+" {
730 if let Some(ref next) = t.peek_token()? {
731 if next.starts_with(|c: char| c.is_ascii_digit()) {
732 let num = t.next_token()?.unwrap();
733 return Ok(PartiqlValue::Literal(AttributeValue::N(format!(
734 "{tok}{num}"
735 ))));
736 }
737 }
738 }
739
740 if tok.starts_with(|c: char| c.is_ascii_digit()) {
742 return Ok(PartiqlValue::Literal(AttributeValue::N(tok)));
743 }
744
745 match tok.to_uppercase().as_str() {
747 "TRUE" => return Ok(PartiqlValue::Literal(AttributeValue::BOOL(true))),
748 "FALSE" => return Ok(PartiqlValue::Literal(AttributeValue::BOOL(false))),
749 "NULL" => return Ok(PartiqlValue::Literal(AttributeValue::NULL(true))),
750 _ => {}
751 }
752
753 if tok
756 .chars()
757 .next()
758 .is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
759 {
760 let mut path = tok.clone();
762 while let Some(ref next) = t.peek_token()? {
763 if next == "." {
764 t.next_token()?;
765 let seg = t.next_token()?.ok_or("Expected attribute name after '.'")?;
766 path.push('.');
767 path.push_str(&unquote(&seg));
768 } else {
769 break;
770 }
771 }
772 return Ok(PartiqlValue::Literal(AttributeValue::S(path)));
773 }
774
775 Err(format!("Unexpected value token: {tok}"))
776}
777
778fn set_literal_to_value(elements: Vec<PartiqlValue>) -> Result<PartiqlValue, String> {
780 if elements.is_empty() {
781 return Err("Set literals cannot be empty".to_string());
782 }
783
784 let first = match &elements[0] {
786 PartiqlValue::Literal(av) => av,
787 PartiqlValue::Parameter(_) => {
788 return Err("Parameter placeholders in set literals are not supported".to_string());
789 }
790 };
791
792 match first {
793 AttributeValue::S(_) => {
794 let mut ss = Vec::new();
795 for elem in &elements {
796 match elem {
797 PartiqlValue::Literal(AttributeValue::S(s)) => ss.push(s.clone()),
798 _ => return Err("Mixed types in string set literal".to_string()),
799 }
800 }
801 Ok(PartiqlValue::Literal(AttributeValue::SS(ss)))
802 }
803 AttributeValue::N(_) => {
804 let mut ns = Vec::new();
805 for elem in &elements {
806 match elem {
807 PartiqlValue::Literal(AttributeValue::N(n)) => ns.push(n.clone()),
808 _ => return Err("Mixed types in number set literal".to_string()),
809 }
810 }
811 Ok(PartiqlValue::Literal(AttributeValue::NS(ns)))
812 }
813 _ => Err(format!(
814 "Unsupported element type in set literal: {first:?}"
815 )),
816 }
817}
818
819fn parse_item_literal(t: &mut Tokenizer) -> Result<HashMap<String, AttributeValue>, String> {
821 let open = t.next_token()?.ok_or("Expected '{'")?;
822 if open != "{" {
823 return Err(format!("Expected '{{' but got '{open}'"));
824 }
825
826 let mut item = HashMap::new();
827
828 loop {
829 let tok = t.peek_token()?.ok_or("Unexpected end of item literal")?;
830 if tok == "}" {
831 t.next_token()?; break;
833 }
834
835 if tok == "," {
837 t.next_token()?;
838 continue;
839 }
840
841 let key_tok = t.next_token()?.ok_or("Expected key in item literal")?;
843 let key = unquote(&key_tok);
844
845 let colon = t.next_token()?.ok_or("Expected ':'")?;
846 if colon != ":" {
847 return Err(format!("Expected ':' but got '{colon}'"));
848 }
849
850 let val = parse_item_value(t)?;
852 item.insert(key, val);
853 }
854
855 Ok(item)
856}
857
858fn parse_item_value(t: &mut Tokenizer) -> Result<AttributeValue, String> {
860 let tok = t.peek_token()?.ok_or("Expected value")?;
861
862 if tok == "{" {
863 let inner = parse_item_literal(t)?;
865 return Ok(AttributeValue::M(inner));
866 }
867
868 if tok == "[" {
869 t.next_token()?; let mut items = Vec::new();
872 loop {
873 let peek = t.peek_token()?.ok_or("Unexpected end of list")?;
874 if peek == "]" {
875 t.next_token()?;
876 break;
877 }
878 if peek == "," {
879 t.next_token()?;
880 continue;
881 }
882 items.push(parse_item_value(t)?);
883 }
884 return Ok(AttributeValue::L(items));
885 }
886
887 if tok == "<" {
889 if let Some(ref next_tok) = t.peek_token_at(1)? {
890 if next_tok == "<" {
891 t.next_token()?; t.next_token()?; let mut elements = Vec::new();
894 loop {
895 let peek = t.peek_token()?.ok_or("Unexpected end of set literal")?;
896 if peek == ">" {
897 t.next_token()?; if t.peek_token()?.as_deref() == Some(">") {
899 t.next_token()?; }
901 break;
902 }
903 if peek == "," {
904 t.next_token()?;
905 continue;
906 }
907 elements.push(parse_item_value(t)?);
908 }
909 return item_value_set_literal(elements);
910 }
911 }
912 }
913
914 let tok = t.next_token()?.ok_or("Expected value")?;
916
917 if tok.starts_with('\'') && tok.ends_with('\'') && tok.len() >= 2 {
919 return Ok(AttributeValue::S(tok[1..tok.len() - 1].to_string()));
920 }
921
922 if tok == "-" || tok == "+" {
924 if let Some(ref next) = t.peek_token()? {
925 if next.starts_with(|c: char| c.is_ascii_digit()) {
926 let num = t.next_token()?.unwrap();
927 return Ok(AttributeValue::N(format!("{tok}{num}")));
928 }
929 }
930 }
931
932 if tok.starts_with(|c: char| c.is_ascii_digit()) {
934 return Ok(AttributeValue::N(tok));
935 }
936
937 match tok.to_uppercase().as_str() {
938 "TRUE" => Ok(AttributeValue::BOOL(true)),
939 "FALSE" => Ok(AttributeValue::BOOL(false)),
940 "NULL" => Ok(AttributeValue::NULL(true)),
941 _ => Err(format!("Unexpected value in item literal: {tok}")),
942 }
943}
944
945fn item_value_set_literal(elements: Vec<AttributeValue>) -> Result<AttributeValue, String> {
947 if elements.is_empty() {
948 return Err("Set literals cannot be empty".to_string());
949 }
950 match &elements[0] {
951 AttributeValue::S(_) => {
952 let mut ss = Vec::new();
953 for e in elements {
954 match e {
955 AttributeValue::S(s) => ss.push(s),
956 _ => return Err("Mixed types in string set literal".to_string()),
957 }
958 }
959 Ok(AttributeValue::SS(ss))
960 }
961 AttributeValue::N(_) => {
962 let mut ns = Vec::new();
963 for e in elements {
964 match e {
965 AttributeValue::N(n) => ns.push(n),
966 _ => return Err("Mixed types in number set literal".to_string()),
967 }
968 }
969 Ok(AttributeValue::NS(ns))
970 }
971 _ => Err(format!(
972 "Unsupported element type in set literal: {:?}",
973 elements[0]
974 )),
975 }
976}
977
978fn parse_item_literal_partiql(t: &mut Tokenizer) -> Result<HashMap<String, PartiqlValue>, String> {
981 let open = t.next_token()?.ok_or("Expected '{'")?;
982 if open != "{" {
983 return Err(format!("Expected '{{' but got '{open}'"));
984 }
985
986 let mut item = HashMap::new();
987
988 loop {
989 let tok = t.peek_token()?.ok_or("Unexpected end of item literal")?;
990 if tok == "}" {
991 t.next_token()?; break;
993 }
994
995 if tok == "," {
997 t.next_token()?;
998 continue;
999 }
1000
1001 let key_tok = t.next_token()?.ok_or("Expected key in item literal")?;
1003 let key = unquote(&key_tok);
1004
1005 let colon = t.next_token()?.ok_or("Expected ':'")?;
1006 if colon != ":" {
1007 return Err(format!("Expected ':' but got '{colon}'"));
1008 }
1009
1010 let val = parse_item_value_partiql(t)?;
1012 item.insert(key, val);
1013 }
1014
1015 Ok(item)
1016}
1017
1018fn parse_item_value_partiql(t: &mut Tokenizer) -> Result<PartiqlValue, String> {
1021 let tok = t.peek_token()?.ok_or("Expected value")?;
1022
1023 if tok == "?" {
1024 t.next_token()?; let idx = t.next_param_index();
1026 return Ok(PartiqlValue::Parameter(idx));
1027 }
1028
1029 if tok == "[" {
1031 return parse_value(t);
1032 }
1033
1034 if tok == "{" {
1036 let inner = parse_item_literal_partiql(t)?;
1038 let mut map = HashMap::new();
1040 for (k, v) in inner {
1041 match v {
1042 PartiqlValue::Literal(av) => {
1043 map.insert(k, av);
1044 }
1045 PartiqlValue::Parameter(_) => {
1046 return Err(
1049 "Parameter placeholders inside nested map literals are not yet fully supported"
1050 .to_string(),
1051 );
1052 }
1053 }
1054 }
1055 return Ok(PartiqlValue::Literal(AttributeValue::M(map)));
1056 }
1057
1058 let av = parse_item_value(t)?;
1061 Ok(PartiqlValue::Literal(av))
1062}
1063
1064fn unquote(s: &str) -> String {
1066 if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
1067 s[1..s.len() - 1].to_string()
1068 } else {
1069 s.to_string()
1070 }
1071}
1072
1073fn expect_keyword(t: &mut Tokenizer, kw: &str) -> Result<(), String> {
1074 let tok = t.next_token()?.ok_or(format!("Expected '{kw}'"))?;
1075 if !tok.eq_ignore_ascii_case(kw) {
1076 return Err(format!("Expected '{kw}' but got '{tok}'"));
1077 }
1078 Ok(())
1079}
1080
1081struct Tokenizer {
1086 tokens: Vec<String>,
1087 pos: usize,
1088 param_counter: usize,
1089}
1090
1091impl Tokenizer {
1092 fn new(input: &str) -> Result<Self, String> {
1093 let tokens = tokenize(input)?;
1094 Ok(Self {
1095 tokens,
1096 pos: 0,
1097 param_counter: 0,
1098 })
1099 }
1100
1101 fn next_token(&mut self) -> Result<Option<String>, String> {
1102 if self.pos >= self.tokens.len() {
1103 return Ok(None);
1104 }
1105 let tok = self.tokens[self.pos].clone();
1106 self.pos += 1;
1107 Ok(Some(tok))
1108 }
1109
1110 fn peek_token(&self) -> Result<Option<String>, String> {
1111 if self.pos >= self.tokens.len() {
1112 return Ok(None);
1113 }
1114 Ok(Some(self.tokens[self.pos].clone()))
1115 }
1116
1117 fn peek_token_at(&self, offset: usize) -> Result<Option<String>, String> {
1119 let idx = self.pos + offset;
1120 if idx >= self.tokens.len() {
1121 return Ok(None);
1122 }
1123 Ok(Some(self.tokens[idx].clone()))
1124 }
1125
1126 fn next_param_index(&mut self) -> usize {
1127 let idx = self.param_counter;
1128 self.param_counter += 1;
1129 idx
1130 }
1131}
1132
1133fn tokenize(input: &str) -> Result<Vec<String>, String> {
1135 let mut tokens = Vec::new();
1136 let chars: Vec<char> = input.chars().collect();
1137 let len = chars.len();
1138 let mut i = 0;
1139
1140 while i < len {
1141 if chars[i].is_ascii_whitespace() {
1143 i += 1;
1144 continue;
1145 }
1146
1147 match chars[i] {
1149 '{' | '}' | '[' | ']' | '(' | ')' | ',' | ':' | '*' | '?' | '+' | '-' | '.' => {
1150 tokens.push(chars[i].to_string());
1152 i += 1;
1153 continue;
1154 }
1155 _ => {}
1156 }
1157
1158 if i + 1 < len {
1160 let two = format!("{}{}", chars[i], chars[i + 1]);
1161 match two.as_str() {
1162 "<>" | "<=" | ">=" | "!=" => {
1163 tokens.push(two);
1164 i += 2;
1165 continue;
1166 }
1167 _ => {}
1168 }
1169 }
1170
1171 if matches!(chars[i], '=' | '<' | '>') {
1173 tokens.push(chars[i].to_string());
1174 i += 1;
1175 continue;
1176 }
1177
1178 if chars[i] == '\'' {
1180 let mut s = String::from('\'');
1181 i += 1;
1182 while i < len {
1183 if chars[i] == '\'' {
1184 if i + 1 < len && chars[i + 1] == '\'' {
1186 s.push('\'');
1187 i += 2;
1188 } else {
1189 break; }
1191 } else {
1192 s.push(chars[i]);
1193 i += 1;
1194 }
1195 }
1196 if i < len {
1197 s.push('\'');
1198 i += 1;
1199 }
1200 tokens.push(s);
1201 continue;
1202 }
1203
1204 if chars[i] == '"' {
1206 let mut s = String::from('"');
1207 i += 1;
1208 while i < len {
1209 if chars[i] == '"' {
1210 if i + 1 < len && chars[i + 1] == '"' {
1212 s.push('"');
1213 i += 2;
1214 } else {
1215 break; }
1217 } else {
1218 s.push(chars[i]);
1219 i += 1;
1220 }
1221 }
1222 if i < len {
1223 s.push('"');
1224 i += 1;
1225 }
1226 tokens.push(s);
1227 continue;
1228 }
1229
1230 if chars[i].is_ascii_digit() {
1232 let mut s = String::new();
1233 while i < len && (chars[i].is_ascii_digit() || chars[i] == '.') {
1234 s.push(chars[i]);
1235 i += 1;
1236 }
1237 tokens.push(s);
1238 continue;
1239 }
1240
1241 if chars[i].is_ascii_alphabetic() || chars[i] == '_' {
1243 let mut s = String::new();
1244 while i < len && (chars[i].is_ascii_alphanumeric() || chars[i] == '_') {
1245 s.push(chars[i]);
1246 i += 1;
1247 }
1248 tokens.push(s);
1249 continue;
1250 }
1251
1252 return Err(format!("Unexpected character: '{}'", chars[i]));
1254 }
1255
1256 Ok(tokens)
1257}
1258
1259#[cfg(test)]
1260mod tests {
1261 use super::*;
1262
1263 #[test]
1264 fn test_parse_select_star() {
1265 let stmt = parse("SELECT * FROM \"TestTable\"").unwrap();
1266 match stmt {
1267 Statement::Select {
1268 table_name,
1269 projections,
1270 where_clause,
1271 } => {
1272 assert_eq!(table_name, "TestTable");
1273 assert!(projections.is_empty());
1274 assert!(where_clause.is_none());
1275 }
1276 _ => panic!("Expected SELECT"),
1277 }
1278 }
1279
1280 #[test]
1281 fn test_parse_select_with_where() {
1282 let stmt = parse("SELECT * FROM \"T\" WHERE pk = 'hello'").unwrap();
1283 match stmt {
1284 Statement::Select {
1285 where_clause: Some(wc),
1286 ..
1287 } => {
1288 assert_eq!(wc.groups[0].len(), 1);
1289 match &wc.groups[0][0] {
1290 WhereCondition::Comparison(c) => {
1291 assert_eq!(c.path, "pk");
1292 assert_eq!(c.op, CompOp::Eq);
1293 }
1294 _ => panic!("Expected Comparison"),
1295 }
1296 }
1297 _ => panic!("Expected SELECT with WHERE"),
1298 }
1299 }
1300
1301 #[test]
1302 fn test_parse_select_with_projection() {
1303 let stmt = parse("SELECT name, age FROM \"Users\"").unwrap();
1304 match stmt {
1305 Statement::Select { projections, .. } => {
1306 assert_eq!(projections, vec!["name", "age"]);
1307 }
1308 _ => panic!("Expected SELECT"),
1309 }
1310 }
1311
1312 #[test]
1313 fn test_parse_insert() {
1314 let stmt =
1315 parse("INSERT INTO \"TestTable\" VALUE {'pk': 'key1', 'data': 'hello'}").unwrap();
1316 match stmt {
1317 Statement::Insert {
1318 table_name, item, ..
1319 } => {
1320 assert_eq!(table_name, "TestTable");
1321 assert_eq!(
1322 item.get("pk"),
1323 Some(&PartiqlValue::Literal(AttributeValue::S(
1324 "key1".to_string()
1325 )))
1326 );
1327 assert_eq!(
1328 item.get("data"),
1329 Some(&PartiqlValue::Literal(AttributeValue::S(
1330 "hello".to_string()
1331 )))
1332 );
1333 }
1334 _ => panic!("Expected INSERT"),
1335 }
1336 }
1337
1338 #[test]
1339 fn test_parse_update() {
1340 let stmt = parse("UPDATE \"T\" SET name = 'Bob' WHERE pk = 'k1'").unwrap();
1341 match stmt {
1342 Statement::Update {
1343 table_name,
1344 set_clauses,
1345 where_clause,
1346 ..
1347 } => {
1348 assert_eq!(table_name, "T");
1349 assert_eq!(set_clauses.len(), 1);
1350 assert_eq!(set_clauses[0].path, "name");
1351 assert!(where_clause.is_some());
1352 }
1353 _ => panic!("Expected UPDATE"),
1354 }
1355 }
1356
1357 #[test]
1358 fn test_parse_delete() {
1359 let stmt = parse("DELETE FROM \"T\" WHERE pk = 'k1'").unwrap();
1360 match stmt {
1361 Statement::Delete {
1362 table_name,
1363 where_clause,
1364 } => {
1365 assert_eq!(table_name, "T");
1366 assert!(where_clause.is_some());
1367 }
1368 _ => panic!("Expected DELETE"),
1369 }
1370 }
1371
1372 #[test]
1373 fn test_parse_parameter() {
1374 let stmt = parse("SELECT * FROM \"T\" WHERE pk = ?").unwrap();
1375 match stmt {
1376 Statement::Select {
1377 where_clause: Some(wc),
1378 ..
1379 } => match &wc.groups[0][0] {
1380 WhereCondition::Comparison(c) => match &c.value {
1381 PartiqlValue::Parameter(0) => {}
1382 other => panic!("Expected Parameter(0), got {other:?}"),
1383 },
1384 _ => panic!("Expected Comparison"),
1385 },
1386 _ => panic!("Expected SELECT with WHERE"),
1387 }
1388 }
1389
1390 #[test]
1391 fn test_parse_numeric_literal() {
1392 let stmt = parse("SELECT * FROM \"T\" WHERE age > 42").unwrap();
1393 match stmt {
1394 Statement::Select {
1395 where_clause: Some(wc),
1396 ..
1397 } => match &wc.groups[0][0] {
1398 WhereCondition::Comparison(c) => {
1399 assert_eq!(c.op, CompOp::Gt);
1400 match &c.value {
1401 PartiqlValue::Literal(AttributeValue::N(n)) => assert_eq!(n, "42"),
1402 other => panic!("Expected N(42), got {other:?}"),
1403 }
1404 }
1405 _ => panic!("Expected Comparison"),
1406 },
1407 _ => panic!("Expected SELECT"),
1408 }
1409 }
1410
1411 #[test]
1412 fn test_parse_insert_with_number() {
1413 let stmt = parse("INSERT INTO \"T\" VALUE {'pk': 'k1', 'age': 25}").unwrap();
1414 match stmt {
1415 Statement::Insert { item, .. } => {
1416 assert_eq!(
1417 item.get("age"),
1418 Some(&PartiqlValue::Literal(AttributeValue::N("25".to_string())))
1419 );
1420 }
1421 _ => panic!("Expected INSERT"),
1422 }
1423 }
1424
1425 #[test]
1426 fn test_invalid_statement() {
1427 let result = parse("MERGE INTO \"T\"");
1428 assert!(result.is_err());
1429 }
1430
1431 #[test]
1432 fn test_empty_statement() {
1433 let result = parse("");
1434 assert!(result.is_err());
1435 }
1436
1437 #[test]
1438 fn test_parse_between() {
1439 let stmt = parse("SELECT * FROM \"T\" WHERE age BETWEEN 18 AND 65").unwrap();
1440 match stmt {
1441 Statement::Select {
1442 where_clause: Some(wc),
1443 ..
1444 } => {
1445 assert_eq!(wc.groups[0].len(), 1);
1446 match &wc.groups[0][0] {
1447 WhereCondition::Between(path, low, high) => {
1448 assert_eq!(path, "age");
1449 match low {
1450 PartiqlValue::Literal(AttributeValue::N(n)) => assert_eq!(n, "18"),
1451 other => panic!("Expected N(18), got {other:?}"),
1452 }
1453 match high {
1454 PartiqlValue::Literal(AttributeValue::N(n)) => assert_eq!(n, "65"),
1455 other => panic!("Expected N(65), got {other:?}"),
1456 }
1457 }
1458 other => panic!("Expected Between, got {other:?}"),
1459 }
1460 }
1461 _ => panic!("Expected SELECT with WHERE"),
1462 }
1463 }
1464
1465 #[test]
1466 fn test_parse_between_and_other_condition() {
1467 let stmt = parse("SELECT * FROM \"T\" WHERE x BETWEEN 1 AND 10 AND y = 'hello'").unwrap();
1468 match stmt {
1469 Statement::Select {
1470 where_clause: Some(wc),
1471 ..
1472 } => {
1473 assert_eq!(wc.groups[0].len(), 2);
1474 assert!(matches!(&wc.groups[0][0], WhereCondition::Between(..)));
1475 assert!(matches!(&wc.groups[0][1], WhereCondition::Comparison(..)));
1476 }
1477 _ => panic!("Expected SELECT with WHERE"),
1478 }
1479 }
1480
1481 #[test]
1482 fn test_parse_in() {
1483 let stmt = parse("SELECT * FROM \"T\" WHERE status IN ('ACTIVE', 'PENDING')").unwrap();
1484 match stmt {
1485 Statement::Select {
1486 where_clause: Some(wc),
1487 ..
1488 } => {
1489 assert_eq!(wc.groups[0].len(), 1);
1490 match &wc.groups[0][0] {
1491 WhereCondition::In(path, values) => {
1492 assert_eq!(path, "status");
1493 assert_eq!(values.len(), 2);
1494 }
1495 other => panic!("Expected In, got {other:?}"),
1496 }
1497 }
1498 _ => panic!("Expected SELECT with WHERE"),
1499 }
1500 }
1501
1502 #[test]
1503 fn test_parse_contains() {
1504 let stmt = parse("SELECT * FROM \"T\" WHERE CONTAINS(name, 'john')").unwrap();
1505 match stmt {
1506 Statement::Select {
1507 where_clause: Some(wc),
1508 ..
1509 } => {
1510 assert_eq!(wc.groups[0].len(), 1);
1511 match &wc.groups[0][0] {
1512 WhereCondition::Contains(path, val) => {
1513 assert_eq!(path, "name");
1514 match val {
1515 PartiqlValue::Literal(AttributeValue::S(s)) => {
1516 assert_eq!(s, "john")
1517 }
1518 other => panic!("Expected S(john), got {other:?}"),
1519 }
1520 }
1521 other => panic!("Expected Contains, got {other:?}"),
1522 }
1523 }
1524 _ => panic!("Expected SELECT with WHERE"),
1525 }
1526 }
1527
1528 #[test]
1529 fn test_parse_is_missing() {
1530 let stmt = parse("SELECT * FROM \"T\" WHERE email IS MISSING").unwrap();
1531 match stmt {
1532 Statement::Select {
1533 where_clause: Some(wc),
1534 ..
1535 } => {
1536 assert_eq!(wc.groups[0].len(), 1);
1537 match &wc.groups[0][0] {
1538 WhereCondition::IsMissing(path) => assert_eq!(path, "email"),
1539 other => panic!("Expected IsMissing, got {other:?}"),
1540 }
1541 }
1542 _ => panic!("Expected SELECT with WHERE"),
1543 }
1544 }
1545
1546 #[test]
1547 fn test_parse_is_not_missing() {
1548 let stmt = parse("SELECT * FROM \"T\" WHERE email IS NOT MISSING").unwrap();
1549 match stmt {
1550 Statement::Select {
1551 where_clause: Some(wc),
1552 ..
1553 } => {
1554 assert_eq!(wc.groups[0].len(), 1);
1555 match &wc.groups[0][0] {
1556 WhereCondition::IsNotMissing(path) => assert_eq!(path, "email"),
1557 other => panic!("Expected IsNotMissing, got {other:?}"),
1558 }
1559 }
1560 _ => panic!("Expected SELECT with WHERE"),
1561 }
1562 }
1563
1564 #[test]
1565 fn test_parse_nested_projection() {
1566 let stmt = parse("SELECT a.b.c, d FROM \"T\"").unwrap();
1567 match stmt {
1568 Statement::Select { projections, .. } => {
1569 assert_eq!(projections, vec!["a.b.c", "d"]);
1570 }
1571 _ => panic!("Expected SELECT"),
1572 }
1573 }
1574
1575 #[test]
1576 fn test_parse_array_index_projection() {
1577 let stmt = parse("SELECT items[0].name FROM \"T\"").unwrap();
1578 match stmt {
1579 Statement::Select { projections, .. } => {
1580 assert_eq!(projections, vec!["items[0].name"]);
1581 }
1582 _ => panic!("Expected SELECT"),
1583 }
1584 }
1585
1586 #[test]
1587 fn test_parse_update_with_remove() {
1588 let stmt =
1589 parse("UPDATE \"T\" SET name = 'Bob' REMOVE age, email WHERE pk = 'k1'").unwrap();
1590 match stmt {
1591 Statement::Update {
1592 set_clauses,
1593 remove_paths,
1594 where_clause,
1595 ..
1596 } => {
1597 assert_eq!(set_clauses.len(), 1);
1598 assert_eq!(remove_paths, vec!["age", "email"]);
1599 assert!(where_clause.is_some());
1600 }
1601 _ => panic!("Expected UPDATE"),
1602 }
1603 }
1604
1605 #[test]
1606 fn test_parse_update_remove_only() {
1607 let stmt = parse("UPDATE \"T\" REMOVE old_field WHERE pk = 'k1'").unwrap();
1608 match stmt {
1609 Statement::Update {
1610 set_clauses,
1611 remove_paths,
1612 ..
1613 } => {
1614 assert!(set_clauses.is_empty());
1615 assert_eq!(remove_paths, vec!["old_field"]);
1616 }
1617 _ => panic!("Expected UPDATE"),
1618 }
1619 }
1620
1621 #[test]
1622 fn test_parse_set_expression_add() {
1623 let stmt = parse("UPDATE \"T\" SET count = count + 1 WHERE pk = 'k1'").unwrap();
1624 match stmt {
1625 Statement::Update { set_clauses, .. } => {
1626 assert_eq!(set_clauses.len(), 1);
1627 match &set_clauses[0].value {
1628 SetValue::Add(attr, val) => {
1629 assert_eq!(attr, "count");
1630 assert_eq!(
1631 val,
1632 &PartiqlValue::Literal(AttributeValue::N("1".to_string()))
1633 );
1634 }
1635 other => panic!("Expected Add, got {other:?}"),
1636 }
1637 }
1638 _ => panic!("Expected UPDATE"),
1639 }
1640 }
1641
1642 #[test]
1643 fn test_parse_count_star() {
1644 let stmt = parse("SELECT COUNT(*) FROM \"T\"").unwrap();
1645 match stmt {
1646 Statement::Select { projections, .. } => {
1647 assert_eq!(projections, vec!["COUNT(*)"]);
1648 }
1649 _ => panic!("Expected SELECT"),
1650 }
1651 }
1652
1653 #[test]
1654 fn test_parse_set_literal() {
1655 let stmt = parse("INSERT INTO \"T\" VALUE {'pk': 'k1', 'tags': <<'a', 'b'>>}").unwrap();
1656 match stmt {
1657 Statement::Insert { item, .. } => match item.get("tags") {
1658 Some(PartiqlValue::Literal(AttributeValue::SS(ss))) => {
1659 assert!(ss.contains(&"a".to_string()));
1660 assert!(ss.contains(&"b".to_string()));
1661 }
1662 other => panic!("Expected SS, got {other:?}"),
1663 },
1664 _ => panic!("Expected INSERT"),
1665 }
1666 }
1667
1668 #[test]
1669 fn test_parse_or_condition() {
1670 let stmt = parse("SELECT * FROM \"T\" WHERE status = 'A' OR status = 'B'").unwrap();
1671 match stmt {
1672 Statement::Select {
1673 where_clause: Some(wc),
1674 ..
1675 } => {
1676 assert_eq!(wc.groups.len(), 2);
1677 assert_eq!(wc.groups[0].len(), 1);
1678 assert_eq!(wc.groups[1].len(), 1);
1679 }
1680 _ => panic!("Expected SELECT with WHERE"),
1681 }
1682 }
1683
1684 #[test]
1685 fn test_parse_and_or_mixed() {
1686 let stmt = parse("SELECT * FROM \"T\" WHERE a = 1 AND b = 2 OR c = 3").unwrap();
1687 match stmt {
1688 Statement::Select {
1689 where_clause: Some(wc),
1690 ..
1691 } => {
1692 assert_eq!(wc.groups.len(), 2);
1693 assert_eq!(wc.groups[0].len(), 2); assert_eq!(wc.groups[1].len(), 1); }
1696 _ => panic!("Expected SELECT with WHERE"),
1697 }
1698 }
1699
1700 #[test]
1701 fn test_parse_insert_if_not_exists() {
1702 let stmt =
1703 parse("INSERT INTO \"T\" VALUE {'pk': 'k1', 'name': 'A'} IF NOT EXISTS").unwrap();
1704 match stmt {
1705 Statement::Insert { if_not_exists, .. } => {
1706 assert!(if_not_exists);
1707 }
1708 _ => panic!("Expected INSERT"),
1709 }
1710 }
1711
1712 #[test]
1713 fn test_parse_nested_path_in_where_function() {
1714 let stmt = parse("SELECT * FROM \"T\" WHERE BEGINS_WITH(address.city, 'Lon')").unwrap();
1715 match stmt {
1716 Statement::Select {
1717 where_clause: Some(wc),
1718 ..
1719 } => match &wc.groups[0][0] {
1720 WhereCondition::BeginsWith(path, _) => {
1721 assert_eq!(path, "address.city");
1722 }
1723 other => panic!("Expected BeginsWith, got {other:?}"),
1724 },
1725 _ => panic!("Expected SELECT with WHERE"),
1726 }
1727 }
1728}