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