1use super::select_ast::*;
18use crate::{Error, Result, TableId, Value};
19
20#[derive(Debug)]
27pub struct SelectParser {
28 current_token: Option<Token>,
30 tokenizer: Tokenizer,
32 next_bind_index: usize,
36}
37
38#[derive(Debug, Clone, PartialEq)]
40pub enum Token {
41 Select,
43 Distinct,
44 From,
45 Where,
46 GroupBy,
47 Having,
48 OrderBy,
49 Limit,
50 PerPartitionLimit,
51 Offset,
52 And,
53 Or,
54 Not,
55 Like,
56 In,
57 Between,
58 As,
59 Asc,
60 Desc,
61 Allow,
62 Filtering,
63 Count,
64 Sum,
65 Avg,
66 Min,
67 Max,
68 Is,
70 Null,
71 Contains,
72 Key,
73 Writetime,
75 Ttl,
76
77 Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual, Plus, Minus, Multiply, Divide, Modulo, Integer(i64),
92 Float(f64),
93 String(String),
94 Boolean(bool),
95 Uuid([u8; 16]),
101 Blob(Vec<u8>),
105
106 Identifier(String),
108
109 LeftParen, RightParen, LeftBracket, RightBracket, LeftBrace, RightBrace, Comma, Semicolon, Dot, Question, Eof,
123 Newline,
124 Whitespace,
125}
126
127fn keyword_for(ident: &str) -> Option<Token> {
132 const KEYWORDS: &[(&str, Token)] = &[
134 ("SELECT", Token::Select),
135 ("DISTINCT", Token::Distinct),
136 ("FROM", Token::From),
137 ("WHERE", Token::Where),
138 ("HAVING", Token::Having),
139 ("LIMIT", Token::Limit),
140 ("OFFSET", Token::Offset),
141 ("AND", Token::And),
142 ("OR", Token::Or),
143 ("NOT", Token::Not),
144 ("LIKE", Token::Like),
145 ("IN", Token::In),
146 ("BETWEEN", Token::Between),
147 ("AS", Token::As),
148 ("ASC", Token::Asc),
149 ("DESC", Token::Desc),
150 ("ALLOW", Token::Allow),
151 ("FILTERING", Token::Filtering),
152 ("COUNT", Token::Count),
153 ("SUM", Token::Sum),
154 ("AVG", Token::Avg),
155 ("MIN", Token::Min),
156 ("MAX", Token::Max),
157 ("IS", Token::Is),
158 ("NULL", Token::Null),
159 ("CONTAINS", Token::Contains),
160 ("KEY", Token::Key),
161 ("WRITETIME", Token::Writetime),
162 ("TTL", Token::Ttl),
163 ("TRUE", Token::Boolean(true)),
164 ("FALSE", Token::Boolean(false)),
165 ];
166
167 KEYWORDS
168 .iter()
169 .find(|(kw, _)| ident.eq_ignore_ascii_case(kw))
170 .map(|(_, tok)| tok.clone())
171}
172
173fn aggregate_for(token: &Token) -> Option<AggregateType> {
175 match token {
176 Token::Count => Some(AggregateType::Count),
177 Token::Sum => Some(AggregateType::Sum),
178 Token::Avg => Some(AggregateType::Avg),
179 Token::Min => Some(AggregateType::Min),
180 Token::Max => Some(AggregateType::Max),
181 _ => None,
182 }
183}
184
185#[derive(Debug)]
187pub struct Tokenizer {
188 input: Vec<char>,
189 position: usize,
190 current_char: Option<char>,
191}
192
193impl Tokenizer {
194 pub fn new(input: &str) -> Self {
195 let chars: Vec<char> = input.chars().collect();
196 let current_char = chars.first().copied();
197
198 Self {
199 input: chars,
200 position: 0,
201 current_char,
202 }
203 }
204
205 fn advance(&mut self) {
206 self.position += 1;
207 self.current_char = self.input.get(self.position).copied();
208 }
209
210 fn peek(&self) -> Option<char> {
211 self.input.get(self.position + 1).copied()
212 }
213
214 fn skip_whitespace(&mut self) {
215 while let Some(ch) = self.current_char {
216 if ch.is_whitespace() {
217 self.advance();
218 } else {
219 break;
220 }
221 }
222 }
223
224 fn read_string(&mut self, quote_char: char) -> Result<String> {
225 let mut value = String::new();
226 self.advance(); while let Some(ch) = self.current_char {
229 if ch == quote_char {
230 self.advance(); return Ok(value);
232 } else if ch == '\\' {
233 self.advance();
234 if let Some(escaped) = self.current_char {
235 let mapped = match escaped {
236 'n' => Some('\n'),
237 't' => Some('\t'),
238 'r' => Some('\r'),
239 '\\' => Some('\\'),
240 '\'' => Some('\''),
241 '"' => Some('"'),
242 _ => None,
243 };
244 match mapped {
245 Some(c) => value.push(c),
246 None => {
247 value.push('\\');
248 value.push(escaped);
249 }
250 }
251 self.advance();
252 }
253 } else {
254 value.push(ch);
255 self.advance();
256 }
257 }
258
259 Err(Error::cql_parse("Unterminated string literal"))
260 }
261
262 fn read_number(&mut self) -> Result<Token> {
263 let mut value = String::new();
264 let mut has_dot = false;
265
266 while let Some(ch) = self.current_char {
267 if ch.is_ascii_digit() {
268 value.push(ch);
269 self.advance();
270 } else if ch == '.' && !has_dot {
271 has_dot = true;
272 value.push(ch);
273 self.advance();
274 } else {
275 break;
276 }
277 }
278
279 if has_dot {
280 value
281 .parse::<f64>()
282 .map(Token::Float)
283 .map_err(|_| Error::cql_parse(format!("Invalid float: {}", value)))
284 } else {
285 value
286 .parse::<i64>()
287 .map(Token::Integer)
288 .map_err(|_| Error::cql_parse(format!("Invalid integer: {}", value)))
289 }
290 }
291
292 fn try_read_uuid(&mut self) -> Option<Token> {
306 const GROUPS: [usize; 5] = [8, 4, 4, 4, 12];
308 const UUID_LEN: usize = 36; let window: Vec<char> = self
312 .input
313 .get(self.position..self.position + UUID_LEN)?
314 .to_vec();
315
316 if let Some(next) = self.input.get(self.position + UUID_LEN) {
320 if next.is_ascii_hexdigit() || next.is_alphanumeric() || *next == '_' || *next == '-' {
321 return None;
322 }
323 }
324
325 let mut bytes = [0u8; 16];
327 let mut byte_idx = 0;
328 let mut hi: Option<u8> = None;
329 let mut idx = 0;
330 for (g, &group_len) in GROUPS.iter().enumerate() {
331 if g > 0 {
332 if window.get(idx) != Some(&'-') {
333 return None;
334 }
335 idx += 1;
336 }
337 for _ in 0..group_len {
338 let nibble = window.get(idx)?.to_digit(16)? as u8;
339 idx += 1;
340 match hi.take() {
341 None => hi = Some(nibble),
342 Some(h) => {
343 bytes[byte_idx] = (h << 4) | nibble;
344 byte_idx += 1;
345 }
346 }
347 }
348 }
349
350 if idx != UUID_LEN || byte_idx != 16 || hi.is_some() {
352 return None;
353 }
354
355 for _ in 0..UUID_LEN {
356 self.advance();
357 }
358 Some(Token::Uuid(bytes))
359 }
360
361 fn read_blob_hex(&mut self) -> Result<Token> {
365 self.advance(); self.advance(); let mut digits = String::new();
369 while let Some(ch) = self.current_char {
370 if ch.is_ascii_hexdigit() {
371 digits.push(ch);
372 self.advance();
373 } else {
374 break;
375 }
376 }
377
378 if digits.len() % 2 != 0 {
379 return Err(Error::cql_parse(format!(
380 "Blob literal must have an even number of hex digits, got {} in 0x{}",
381 digits.len(),
382 digits
383 )));
384 }
385
386 let mut bytes = Vec::with_capacity(digits.len() / 2);
387 let chars: Vec<char> = digits.chars().collect();
388 for pair in chars.chunks(2) {
389 let hi = pair[0]
390 .to_digit(16)
391 .ok_or_else(|| Error::cql_parse("Invalid hex digit in blob literal"))?
392 as u8;
393 let lo = pair[1]
394 .to_digit(16)
395 .ok_or_else(|| Error::cql_parse("Invalid hex digit in blob literal"))?
396 as u8;
397 bytes.push((hi << 4) | lo);
398 }
399
400 Ok(Token::Blob(bytes))
401 }
402
403 fn read_identifier(&mut self) -> String {
404 let mut value = String::new();
405
406 while let Some(ch) = self.current_char {
407 if ch.is_alphanumeric() || ch == '_' {
408 value.push(ch);
409 self.advance();
410 } else {
411 break;
412 }
413 }
414
415 value
416 }
417
418 fn expect_by_keyword(&mut self, after: &str) -> Result<()> {
420 self.expect_keyword_word("BY", after)
421 }
422
423 fn expect_keyword_word(&mut self, word: &str, after: &str) -> Result<()> {
427 self.skip_whitespace();
428 let next = self.read_identifier();
429 if next.eq_ignore_ascii_case(word) {
430 Ok(())
431 } else {
432 Err(Error::cql_parse(format!(
433 "Expected {} after {}",
434 word, after
435 )))
436 }
437 }
438
439 pub fn next_token(&mut self) -> Result<Token> {
440 loop {
441 let ch = match self.current_char {
442 None => return Ok(Token::Eof),
443 Some(c) => c,
444 };
445
446 let single = match ch {
448 '(' => Some(Token::LeftParen),
449 ')' => Some(Token::RightParen),
450 '[' => Some(Token::LeftBracket),
451 ']' => Some(Token::RightBracket),
452 '{' => Some(Token::LeftBrace),
453 '}' => Some(Token::RightBrace),
454 ',' => Some(Token::Comma),
455 ';' => Some(Token::Semicolon),
456 '.' => Some(Token::Dot),
457 '?' => Some(Token::Question),
458 '+' => Some(Token::Plus),
459 '-' => Some(Token::Minus),
460 '*' => Some(Token::Multiply),
461 '/' => Some(Token::Divide),
462 '%' => Some(Token::Modulo),
463 '=' => Some(Token::Equal),
464 _ => None,
465 };
466 if let Some(tok) = single {
467 self.advance();
468 return Ok(tok);
469 }
470
471 match ch {
472 c if c.is_whitespace() => self.skip_whitespace(),
473 '!' => {
474 if self.peek() == Some('=') {
475 self.advance();
476 self.advance();
477 return Ok(Token::NotEqual);
478 }
479 return Err(Error::cql_parse("Unexpected character: !"));
480 }
481 '<' => {
482 return Ok(match self.peek() {
483 Some('=') => {
484 self.advance();
485 self.advance();
486 Token::LessThanEqual
487 }
488 Some('>') => {
489 self.advance();
490 self.advance();
491 Token::NotEqual
492 }
493 _ => {
494 self.advance();
495 Token::LessThan
496 }
497 });
498 }
499 '>' => {
500 return Ok(if self.peek() == Some('=') {
501 self.advance();
502 self.advance();
503 Token::GreaterThanEqual
504 } else {
505 self.advance();
506 Token::GreaterThan
507 });
508 }
509 '\'' | '"' => return self.read_string(ch).map(Token::String),
510 c if c.is_ascii_hexdigit() => {
516 if let Some(tok) = self.try_read_uuid() {
517 return Ok(tok);
518 }
519 if c == '0' && matches!(self.peek(), Some('x') | Some('X')) {
521 return self.read_blob_hex();
522 }
523 if c.is_ascii_digit() {
524 return self.read_number();
525 }
526 let identifier = self.read_identifier();
529 return self.classify_identifier(identifier);
530 }
531 c if c.is_ascii_digit() => return self.read_number(),
532 c if c.is_alphabetic() || c == '_' => {
533 let identifier = self.read_identifier();
534 return self.classify_identifier(identifier);
535 }
536 other => return Err(Error::cql_parse(format!("Unexpected character: {}", other))),
537 }
538 }
539 }
540
541 fn classify_identifier(&mut self, identifier: String) -> Result<Token> {
546 if identifier.eq_ignore_ascii_case("GROUP") {
549 self.expect_by_keyword("GROUP")?;
550 return Ok(Token::GroupBy);
551 }
552 if identifier.eq_ignore_ascii_case("ORDER") {
553 self.expect_by_keyword("ORDER")?;
554 return Ok(Token::OrderBy);
555 }
556 if identifier.eq_ignore_ascii_case("PER") {
559 self.expect_keyword_word("PARTITION", "PER")?;
560 self.expect_keyword_word("LIMIT", "PER PARTITION")?;
561 return Ok(Token::PerPartitionLimit);
562 }
563 Ok(keyword_for(&identifier).unwrap_or(Token::Identifier(identifier)))
564 }
565}
566
567impl SelectParser {
568 pub fn new(cql: &str) -> Result<Self> {
570 let mut tokenizer = Tokenizer::new(cql);
571 let current_token = Some(tokenizer.next_token()?);
572 Ok(Self {
573 current_token,
574 tokenizer,
575 next_bind_index: 0,
576 })
577 }
578
579 fn advance(&mut self) -> Result<()> {
581 self.current_token = Some(self.tokenizer.next_token()?);
582 Ok(())
583 }
584
585 fn peek(&self) -> &Token {
588 self.current_token.as_ref().unwrap_or(&Token::Eof)
589 }
590
591 fn at(&self, tok: &Token) -> bool {
593 self.current_token
594 .as_ref()
595 .is_some_and(|cur| std::mem::discriminant(cur) == std::mem::discriminant(tok))
596 }
597
598 fn eat(&mut self, tok: &Token) -> Result<bool> {
600 if self.at(tok) {
601 self.advance()?;
602 Ok(true)
603 } else {
604 Ok(false)
605 }
606 }
607
608 fn expect(&mut self, expected: Token) -> Result<()> {
610 if let Some(ref current) = self.current_token {
611 if std::mem::discriminant(current) == std::mem::discriminant(&expected) {
612 self.advance()?;
613 Ok(())
614 } else {
615 Err(Error::cql_parse(format!(
616 "Expected {:?}, found {:?}",
617 expected, current
618 )))
619 }
620 } else {
621 Err(Error::cql_parse("Unexpected end of input"))
622 }
623 }
624
625 fn expect_integer(&mut self, context: &str) -> Result<i64> {
627 if let Some(Token::Integer(n)) = self.current_token {
628 self.advance()?;
629 Ok(n)
630 } else {
631 Err(Error::cql_parse(format!(
632 "Expected integer after {}",
633 context
634 )))
635 }
636 }
637
638 fn parse_column_ref(&mut self, table_or_column: String) -> Result<ColumnRef> {
641 if !self.eat(&Token::Dot)? {
643 return Ok(ColumnRef::new(table_or_column));
644 }
645 if let Some(Token::Identifier(column)) = self.current_token.clone() {
646 self.advance()?;
647 Ok(ColumnRef::qualified(table_or_column, column))
648 } else {
649 Err(Error::cql_parse(
650 "Expected column name after table qualifier",
651 ))
652 }
653 }
654
655 pub fn parse_select_statement(&mut self) -> Result<SelectStatement> {
657 self.expect(Token::Select)?;
658 let select_clause = self.parse_select_clause()?;
659
660 let from_clause = if self.eat(&Token::From)? {
661 Some(self.parse_from_clause()?)
662 } else {
663 None
664 };
665
666 let where_clause = if self.eat(&Token::Where)? {
667 Some(self.parse_where_expression()?)
668 } else {
669 None
670 };
671
672 let group_by = if self.eat(&Token::GroupBy)? {
673 Some(self.parse_group_by_clause()?)
674 } else {
675 None
676 };
677
678 let having_clause = if self.eat(&Token::Having)? {
679 Some(self.parse_where_expression()?)
680 } else {
681 None
682 };
683
684 let order_by = if self.eat(&Token::OrderBy)? {
685 Some(self.parse_order_by_clause()?)
686 } else {
687 None
688 };
689
690 let per_partition_limit = if self.eat(&Token::PerPartitionLimit)? {
692 Some(self.parse_positive_limit("PER PARTITION LIMIT")?)
693 } else {
694 None
695 };
696
697 let limit = if self.eat(&Token::Limit)? {
698 Some(self.parse_limit_clause()?)
699 } else {
700 None
701 };
702
703 let offset = if self.eat(&Token::Offset)? {
704 Some(self.expect_integer("OFFSET")? as u64)
705 } else {
706 None
707 };
708
709 let allow_filtering = if self.eat(&Token::Allow)? {
710 self.expect(Token::Filtering)?;
711 true
712 } else {
713 false
714 };
715
716 if self.at(&Token::PerPartitionLimit) {
721 return Err(Error::cql_parse(
722 "PER PARTITION LIMIT must appear before LIMIT",
723 ));
724 }
725
726 Ok(SelectStatement {
727 select_clause,
728 from_clause,
729 where_clause,
730 group_by,
731 having_clause,
732 order_by,
733 limit,
734 per_partition_limit,
735 offset,
736 allow_filtering,
737 })
738 }
739
740 fn parse_select_clause(&mut self) -> Result<SelectClause> {
742 let distinct = self.eat(&Token::Distinct)?;
743
744 if self.eat(&Token::Multiply)? {
745 return Ok(SelectClause::All);
746 }
747
748 let mut expressions = Vec::new();
749 loop {
750 expressions.push(self.parse_select_expression()?);
751 if !self.eat(&Token::Comma)? {
752 break;
753 }
754 }
755
756 if distinct {
757 Ok(SelectClause::Distinct(expressions))
758 } else {
759 Ok(SelectClause::Columns(expressions))
760 }
761 }
762
763 fn parse_select_expression(&mut self) -> Result<SelectExpression> {
765 let expr = self.parse_primary_expression()?;
766
767 if self.eat(&Token::As)? {
769 if let Some(Token::Identifier(alias)) = self.current_token.clone() {
770 self.advance()?;
771 return Ok(SelectExpression::Aliased(Box::new(expr), alias));
772 }
773 return Err(Error::cql_parse("Expected alias name after AS"));
774 }
775
776 Ok(expr)
777 }
778
779 fn parse_primary_expression(&mut self) -> Result<SelectExpression> {
781 if let Some(agg) = aggregate_for(self.peek()) {
782 self.advance()?;
783 return self.parse_aggregate_function(agg);
784 }
785
786 if matches!(self.peek(), Token::Writetime | Token::Ttl) {
790 let function = match self.current_token.clone() {
791 Some(Token::Writetime) => WriteTimeTtlFunction::WriteTime,
792 Some(Token::Ttl) => WriteTimeTtlFunction::Ttl,
793 _ => unreachable!("peek guard ensures only Writetime or Ttl here"),
794 };
795 self.advance()?;
796 return self.parse_writetime_ttl_call(function);
797 }
798
799 if matches!(self.peek(), Token::Minus) {
804 self.advance()?;
805 return match self.current_token.clone() {
806 Some(Token::Integer(n)) => {
807 self.advance()?;
808 Ok(SelectExpression::Literal(Value::BigInt(-n)))
809 }
810 Some(Token::Float(f)) => {
811 self.advance()?;
812 Ok(SelectExpression::Literal(Value::Float(-f)))
813 }
814 other => Err(Error::cql_parse(format!(
815 "Expected a numeric literal after unary minus, found: {other:?}"
816 ))),
817 };
818 }
819
820 match self.current_token.clone() {
822 Some(Token::Identifier(name)) => {
823 self.advance()?;
824
825 if self.eat(&Token::LeftParen)? {
827 let mut args = Vec::new();
828 if !self.at(&Token::RightParen) {
829 loop {
830 args.push(self.parse_select_expression()?);
831 if !self.eat(&Token::Comma)? {
832 break;
833 }
834 }
835 }
836 self.expect(Token::RightParen)?;
837 return Ok(SelectExpression::Function(FunctionCall { name, args }));
838 }
839
840 let col = self.parse_column_ref(name)?;
842 Ok(SelectExpression::Column(col))
843 }
844 Some(Token::Integer(n)) => {
845 self.advance()?;
846 Ok(SelectExpression::Literal(Value::BigInt(n)))
847 }
848 Some(Token::Float(f)) => {
849 self.advance()?;
850 Ok(SelectExpression::Literal(Value::Float(f)))
851 }
852 Some(Token::String(s)) => {
853 self.advance()?;
854 Ok(SelectExpression::Literal(Value::Text(s)))
855 }
856 Some(Token::Uuid(bytes)) => {
857 self.advance()?;
858 Ok(SelectExpression::Literal(Value::Uuid(bytes)))
859 }
860 Some(Token::Blob(bytes)) => {
861 self.advance()?;
862 Ok(SelectExpression::Literal(Value::Blob(bytes)))
863 }
864 Some(Token::Boolean(b)) => {
865 self.advance()?;
866 Ok(SelectExpression::Literal(Value::Boolean(b)))
867 }
868 Some(Token::Null) => {
869 self.advance()?;
870 Ok(SelectExpression::Literal(Value::Null))
871 }
872 Some(Token::Question) => {
877 let index = self.next_bind_index;
878 self.next_bind_index += 1;
879 self.advance()?;
880 Ok(SelectExpression::BindMarker(index))
881 }
882 Some(Token::LeftParen) => {
883 self.advance()?;
884 let expr = self.parse_select_expression()?;
885 self.expect(Token::RightParen)?;
886 Ok(expr)
887 }
888 other => Err(Error::cql_parse(format!(
889 "Unexpected token in expression: {:?}",
890 other
891 ))),
892 }
893 }
894
895 fn parse_writetime_ttl_call(
900 &mut self,
901 function: WriteTimeTtlFunction,
902 ) -> Result<SelectExpression> {
903 self.expect(Token::LeftParen)?;
904
905 let column = match self.current_token.clone() {
907 Some(Token::Identifier(name)) => {
908 self.advance()?;
909 name
910 }
911 other => {
912 return Err(Error::cql_parse(format!(
913 "{} requires a single column name argument, found: {:?}",
914 match function {
915 WriteTimeTtlFunction::WriteTime => "WRITETIME",
916 WriteTimeTtlFunction::Ttl => "TTL",
917 },
918 other
919 )));
920 }
921 };
922
923 self.expect(Token::RightParen)?;
924
925 let alias = if self.eat(&Token::As)? {
932 match self.current_token.clone() {
933 Some(Token::Identifier(alias_name)) => {
934 self.advance()?;
935 Some(alias_name)
936 }
937 other => {
938 return Err(Error::cql_parse(format!(
939 "Expected alias identifier after AS, found: {:?}",
940 other
941 )));
942 }
943 }
944 } else {
945 None
946 };
947
948 Ok(SelectExpression::WriteTimeTtl(WriteTimeTtlCall {
949 function,
950 column,
951 alias,
952 }))
953 }
954
955 fn parse_aggregate_function(&mut self, agg_type: AggregateType) -> Result<SelectExpression> {
957 self.expect(Token::LeftParen)?;
958
959 let distinct = self.eat(&Token::Distinct)?;
960 let mut args = Vec::new();
961
962 if !self.at(&Token::RightParen) {
963 if self.eat(&Token::Multiply)? {
965 args.push(SelectExpression::Column(ColumnRef::new("*".to_string())));
966 } else {
967 loop {
968 args.push(self.parse_select_expression()?);
969 if !self.eat(&Token::Comma)? {
970 break;
971 }
972 }
973 }
974 }
975
976 self.expect(Token::RightParen)?;
977
978 Ok(SelectExpression::Aggregate(AggregateFunction {
979 function: agg_type,
980 args,
981 distinct,
982 }))
983 }
984
985 fn parse_from_clause(&mut self) -> Result<FromClause> {
987 let Some(Token::Identifier(first_identifier)) = self.current_token.clone() else {
989 return Err(Error::cql_parse("Expected table name in FROM clause"));
990 };
991 self.advance()?;
992
993 let table_name = if self.eat(&Token::Dot)? {
995 if let Some(Token::Identifier(actual_table)) = self.current_token.clone() {
996 self.advance()?;
997 format!("{}.{}", first_identifier, actual_table)
998 } else {
999 return Err(Error::cql_parse("Expected table name after keyspace"));
1000 }
1001 } else {
1002 first_identifier
1003 };
1004
1005 let table = TableId::new(table_name);
1006
1007 const CLAUSE_KEYWORDS: &[&str] = &["WHERE", "GROUP", "ORDER", "HAVING", "LIMIT"];
1012 if let Some(Token::Identifier(alias)) = self.current_token.clone() {
1013 let is_clause_kw = CLAUSE_KEYWORDS
1014 .iter()
1015 .any(|kw| alias.eq_ignore_ascii_case(kw));
1016 if !is_clause_kw {
1017 self.advance()?;
1018 return Ok(FromClause::TableAlias(table, alias));
1019 }
1020 }
1021
1022 Ok(FromClause::Table(table))
1023 }
1024
1025 fn parse_where_expression(&mut self) -> Result<WhereExpression> {
1027 self.parse_or_expression()
1028 }
1029
1030 fn parse_or_expression(&mut self) -> Result<WhereExpression> {
1032 let first = self.parse_and_expression()?;
1033 let mut or_exprs = vec![first];
1034 while self.eat(&Token::Or)? {
1035 or_exprs.push(self.parse_and_expression()?);
1036 }
1037 Ok(unwrap_singleton(or_exprs, WhereExpression::Or))
1038 }
1039
1040 fn parse_and_expression(&mut self) -> Result<WhereExpression> {
1042 let first = self.parse_not_expression()?;
1043 let mut and_exprs = vec![first];
1044 while self.eat(&Token::And)? {
1045 and_exprs.push(self.parse_not_expression()?);
1046 }
1047 Ok(unwrap_singleton(and_exprs, WhereExpression::And))
1048 }
1049
1050 fn parse_not_expression(&mut self) -> Result<WhereExpression> {
1052 if self.eat(&Token::Not)? {
1053 let expr = self.parse_comparison_expression()?;
1054 Ok(WhereExpression::Not(Box::new(expr)))
1055 } else {
1056 self.parse_comparison_expression()
1057 }
1058 }
1059
1060 fn parse_comparison_expression(&mut self) -> Result<WhereExpression> {
1062 if self.eat(&Token::LeftParen)? {
1063 let expr = self.parse_where_expression()?;
1064 self.expect(Token::RightParen)?;
1065 return Ok(WhereExpression::Parentheses(Box::new(expr)));
1066 }
1067
1068 let left = self.parse_select_expression()?;
1069
1070 let simple_op = match self.peek() {
1074 Token::Equal => Some(ComparisonOperator::Equal),
1075 Token::NotEqual => Some(ComparisonOperator::NotEqual),
1076 Token::LessThan => Some(ComparisonOperator::LessThan),
1077 Token::LessThanEqual => Some(ComparisonOperator::LessThanOrEqual),
1078 Token::GreaterThan => Some(ComparisonOperator::GreaterThan),
1079 Token::GreaterThanEqual => Some(ComparisonOperator::GreaterThanOrEqual),
1080 Token::Like => Some(ComparisonOperator::Like),
1081 _ => None,
1082 };
1083
1084 if let Some(op) = simple_op {
1085 self.advance()?;
1086 let right = ComparisonRightSide::Value(self.parse_select_expression()?);
1087 return Ok(WhereExpression::Comparison(ComparisonExpression {
1088 left,
1089 operator: op,
1090 right,
1091 }));
1092 }
1093
1094 let operator = match self.peek() {
1095 Token::In => {
1096 self.advance()?;
1097 let right = self.parse_in_expression()?;
1098 return Ok(WhereExpression::Comparison(ComparisonExpression {
1099 left,
1100 operator: ComparisonOperator::In,
1101 right,
1102 }));
1103 }
1104 Token::Between => {
1105 self.advance()?;
1106 let start = self.parse_select_expression()?;
1107 self.expect(Token::And)?;
1108 let end = self.parse_select_expression()?;
1109 return Ok(WhereExpression::Comparison(ComparisonExpression {
1110 left,
1111 operator: ComparisonOperator::Between,
1112 right: ComparisonRightSide::Range(start, end),
1113 }));
1114 }
1115 Token::Is => {
1116 self.advance()?;
1117 let op = if self.eat(&Token::Not)? {
1118 ComparisonOperator::IsNotNull
1119 } else {
1120 ComparisonOperator::IsNull
1121 };
1122 self.expect(Token::Null)?;
1123 op
1124 }
1125 Token::Contains => {
1126 self.advance()?;
1127 if self.eat(&Token::Key)? {
1128 ComparisonOperator::ContainsKey
1129 } else {
1130 ComparisonOperator::Contains
1131 }
1132 }
1133 other => {
1134 return Err(Error::cql_parse(format!(
1135 "Expected comparison operator, found {:?}",
1136 other
1137 )));
1138 }
1139 };
1140
1141 let right = match operator {
1143 ComparisonOperator::IsNull | ComparisonOperator::IsNotNull => {
1144 ComparisonRightSide::Value(SelectExpression::Literal(Value::Null))
1145 }
1146 _ => ComparisonRightSide::Value(self.parse_select_expression()?),
1147 };
1148
1149 Ok(WhereExpression::Comparison(ComparisonExpression {
1150 left,
1151 operator,
1152 right,
1153 }))
1154 }
1155
1156 fn parse_in_expression(&mut self) -> Result<ComparisonRightSide> {
1158 self.expect(Token::LeftParen)?;
1159 let mut values = Vec::new();
1160
1161 if !self.at(&Token::RightParen) {
1162 loop {
1163 values.push(self.parse_select_expression()?);
1164 if !self.eat(&Token::Comma)? {
1165 break;
1166 }
1167 }
1168 }
1169
1170 self.expect(Token::RightParen)?;
1171 Ok(ComparisonRightSide::ValueList(values))
1172 }
1173
1174 fn parse_group_by_clause(&mut self) -> Result<GroupByClause> {
1176 let mut columns = Vec::new();
1177
1178 loop {
1179 let Some(Token::Identifier(name)) = self.current_token.clone() else {
1180 return Err(Error::cql_parse("Expected column name in GROUP BY"));
1181 };
1182 self.advance()?;
1183 columns.push(self.parse_column_ref(name)?);
1184
1185 if !self.eat(&Token::Comma)? {
1186 break;
1187 }
1188 }
1189
1190 Ok(GroupByClause { columns })
1191 }
1192
1193 fn parse_order_by_clause(&mut self) -> Result<OrderByClause> {
1195 let mut items = Vec::new();
1196
1197 loop {
1198 let expression = self.parse_select_expression()?;
1199
1200 let direction = if self.eat(&Token::Desc)? {
1201 SortDirection::Descending
1202 } else if self.eat(&Token::Asc)? {
1203 SortDirection::Ascending
1204 } else {
1205 SortDirection::Ascending
1206 };
1207
1208 items.push(OrderByItem {
1209 expression,
1210 direction,
1211 });
1212
1213 if !self.eat(&Token::Comma)? {
1214 break;
1215 }
1216 }
1217
1218 Ok(OrderByClause { items })
1219 }
1220
1221 fn parse_limit_clause(&mut self) -> Result<LimitClause> {
1228 let count = self.expect_integer("LIMIT")? as u64;
1229 Ok(LimitClause { count })
1230 }
1231
1232 fn parse_positive_limit(&mut self, clause: &str) -> Result<u64> {
1235 let value = self.expect_integer(clause)?;
1236 if value < 1 {
1237 return Err(Error::cql_parse(format!(
1238 "{} must be a positive integer, got {}",
1239 clause, value
1240 )));
1241 }
1242 Ok(value as u64)
1243 }
1244}
1245
1246fn unwrap_singleton<F>(mut exprs: Vec<WhereExpression>, wrap: F) -> WhereExpression
1250where
1251 F: FnOnce(Vec<WhereExpression>) -> WhereExpression,
1252{
1253 if exprs.len() == 1 {
1254 exprs.pop().expect("checked len == 1")
1255 } else {
1256 wrap(exprs)
1257 }
1258}
1259
1260pub fn parse_select(cql: &str) -> Result<SelectStatement> {
1262 let mut parser = SelectParser::new(cql)?;
1263 parser.parse_select_statement()
1264}
1265
1266#[cfg(all(test, feature = "state_machine"))]
1267mod tests {
1268 use super::super::select_ast::{SelectExpression, WriteTimeTtlFunction};
1269 use super::*;
1270
1271 #[test]
1274 fn test_writetime_basic() {
1275 let stmt = parse_select("SELECT WRITETIME(name) FROM ks.tbl").unwrap();
1276 if let SelectClause::Columns(exprs) = stmt.select_clause {
1277 assert_eq!(exprs.len(), 1);
1278 match &exprs[0] {
1279 SelectExpression::WriteTimeTtl(call) => {
1280 assert_eq!(call.function, WriteTimeTtlFunction::WriteTime);
1281 assert_eq!(call.column, "name");
1282 assert!(call.alias.is_none());
1283 }
1284 other => panic!("Expected WriteTimeTtl, got: {:?}", other),
1285 }
1286 } else {
1287 panic!("Expected Columns select clause");
1288 }
1289 }
1290
1291 #[test]
1292 fn test_ttl_basic() {
1293 let stmt = parse_select("SELECT TTL(name) FROM ks.tbl").unwrap();
1294 if let SelectClause::Columns(exprs) = stmt.select_clause {
1295 assert_eq!(exprs.len(), 1);
1296 match &exprs[0] {
1297 SelectExpression::WriteTimeTtl(call) => {
1298 assert_eq!(call.function, WriteTimeTtlFunction::Ttl);
1299 assert_eq!(call.column, "name");
1300 assert!(call.alias.is_none());
1301 }
1302 other => panic!("Expected WriteTimeTtl, got: {:?}", other),
1303 }
1304 } else {
1305 panic!("Expected Columns select clause");
1306 }
1307 }
1308
1309 #[test]
1310 fn test_writetime_lowercase() {
1311 let stmt = parse_select("SELECT writetime(name) FROM tbl").unwrap();
1313 if let SelectClause::Columns(exprs) = stmt.select_clause {
1314 match &exprs[0] {
1315 SelectExpression::WriteTimeTtl(call) => {
1316 assert_eq!(call.function, WriteTimeTtlFunction::WriteTime);
1317 }
1318 other => panic!("Expected WriteTimeTtl, got: {:?}", other),
1319 }
1320 } else {
1321 panic!("Expected Columns");
1322 }
1323 }
1324
1325 #[test]
1326 fn test_ttl_lowercase() {
1327 let stmt = parse_select("SELECT ttl(name) FROM tbl").unwrap();
1328 if let SelectClause::Columns(exprs) = stmt.select_clause {
1329 match &exprs[0] {
1330 SelectExpression::WriteTimeTtl(call) => {
1331 assert_eq!(call.function, WriteTimeTtlFunction::Ttl);
1332 }
1333 other => panic!("Expected WriteTimeTtl, got: {:?}", other),
1334 }
1335 } else {
1336 panic!("Expected Columns");
1337 }
1338 }
1339
1340 #[test]
1341 fn test_writetime_mixed_case() {
1342 let stmt = parse_select("SELECT WriteTime(name) FROM tbl").unwrap();
1343 if let SelectClause::Columns(exprs) = stmt.select_clause {
1344 match &exprs[0] {
1345 SelectExpression::WriteTimeTtl(call) => {
1346 assert_eq!(call.function, WriteTimeTtlFunction::WriteTime);
1347 }
1348 other => panic!("Expected WriteTimeTtl, got: {:?}", other),
1349 }
1350 } else {
1351 panic!("Expected Columns");
1352 }
1353 }
1354
1355 #[test]
1356 fn test_writetime_and_ttl_together() {
1357 let stmt = parse_select("SELECT WRITETIME(name), TTL(name) FROM ks.tbl").unwrap();
1358 if let SelectClause::Columns(exprs) = stmt.select_clause {
1359 assert_eq!(exprs.len(), 2);
1360 match &exprs[0] {
1361 SelectExpression::WriteTimeTtl(c) => {
1362 assert_eq!(c.function, WriteTimeTtlFunction::WriteTime);
1363 }
1364 other => panic!("Expected WriteTimeTtl for first expr, got: {:?}", other),
1365 }
1366 match &exprs[1] {
1367 SelectExpression::WriteTimeTtl(c) => {
1368 assert_eq!(c.function, WriteTimeTtlFunction::Ttl);
1369 }
1370 other => panic!("Expected WriteTimeTtl for second expr, got: {:?}", other),
1371 }
1372 } else {
1373 panic!("Expected Columns");
1374 }
1375 }
1376
1377 #[test]
1378 fn test_writetime_with_alias() {
1379 let stmt = parse_select("SELECT WRITETIME(name) AS wt FROM tbl").unwrap();
1381 if let SelectClause::Columns(exprs) = stmt.select_clause {
1382 assert_eq!(exprs.len(), 1);
1383 match &exprs[0] {
1384 SelectExpression::WriteTimeTtl(call) => {
1385 assert_eq!(call.function, WriteTimeTtlFunction::WriteTime);
1386 assert_eq!(call.alias.as_deref(), Some("wt"));
1387 }
1388 other => panic!("Expected WriteTimeTtl with alias, got: {:?}", other),
1389 }
1390 } else {
1391 panic!("Expected Columns");
1392 }
1393 }
1394
1395 #[test]
1396 fn test_ttl_with_alias() {
1397 let stmt = parse_select("SELECT ttl(name) AS remaining FROM tbl").unwrap();
1398 if let SelectClause::Columns(exprs) = stmt.select_clause {
1399 match &exprs[0] {
1400 SelectExpression::WriteTimeTtl(call) => {
1401 assert_eq!(call.function, WriteTimeTtlFunction::Ttl);
1402 assert_eq!(call.alias.as_deref(), Some("remaining"));
1403 }
1404 other => panic!("Expected WriteTimeTtl, got: {:?}", other),
1405 }
1406 } else {
1407 panic!("Expected Columns");
1408 }
1409 }
1410
1411 #[test]
1412 fn test_writetime_alongside_plain_columns() {
1413 let stmt = parse_select("SELECT id, WRITETIME(name), name FROM tbl").unwrap();
1414 if let SelectClause::Columns(exprs) = stmt.select_clause {
1415 assert_eq!(exprs.len(), 3);
1416 assert!(matches!(&exprs[0], SelectExpression::Column(_)));
1417 assert!(matches!(&exprs[1], SelectExpression::WriteTimeTtl(_)));
1418 assert!(matches!(&exprs[2], SelectExpression::Column(_)));
1419 } else {
1420 panic!("Expected Columns");
1421 }
1422 }
1423
1424 #[test]
1425 fn test_column_name_is_preserved() {
1426 let stmt = parse_select("SELECT WRITETIME(myColumn) FROM tbl").unwrap();
1427 if let SelectClause::Columns(exprs) = stmt.select_clause {
1428 match &exprs[0] {
1429 SelectExpression::WriteTimeTtl(call) => {
1430 assert_eq!(call.column, "myColumn");
1432 }
1433 other => panic!("Unexpected: {:?}", other),
1434 }
1435 } else {
1436 panic!("Expected Columns");
1437 }
1438 }
1439
1440 #[test]
1443 fn test_simple_select() {
1444 let stmt = parse_select("SELECT * FROM users").unwrap();
1445 assert_eq!(stmt.select_clause, SelectClause::All);
1446 if let Some(FromClause::Table(table)) = stmt.from_clause {
1447 assert_eq!(table.name(), "users");
1448 } else {
1449 panic!("Expected Table in FROM clause");
1450 }
1451 }
1452
1453 #[test]
1454 fn test_select_with_columns() {
1455 let stmt = parse_select("SELECT id, name, email FROM users").unwrap();
1456 if let SelectClause::Columns(exprs) = stmt.select_clause {
1457 assert_eq!(exprs.len(), 3);
1458 } else {
1459 panic!("Expected Columns in SELECT clause");
1460 }
1461 }
1462
1463 #[test]
1464 fn test_select_constant() {
1465 let stmt = parse_select("SELECT 1").unwrap();
1466 assert!(stmt.from_clause.is_none());
1467 if let SelectClause::Columns(exprs) = stmt.select_clause {
1468 assert_eq!(exprs.len(), 1);
1469 if let SelectExpression::Literal(Value::BigInt(1)) = &exprs[0] {
1470 } else {
1472 panic!("Expected literal BigInt 1, got: {:?}", &exprs[0]);
1473 }
1474 } else {
1475 panic!("Expected Columns in SELECT clause");
1476 }
1477 }
1478
1479 #[test]
1480 fn test_select_with_where() {
1481 let stmt = parse_select("SELECT * FROM users WHERE id = 123").unwrap();
1482 assert!(stmt.where_clause.is_some());
1483 }
1484
1485 #[test]
1486 fn test_select_with_aggregates() {
1487 let stmt = parse_select("SELECT COUNT(*), AVG(age) FROM users GROUP BY city").unwrap();
1488 assert!(stmt.requires_aggregation());
1489 assert!(stmt.group_by.is_some());
1490 }
1491
1492 #[test]
1493 fn test_complex_where_clause() {
1494 let stmt =
1495 parse_select("SELECT * FROM users WHERE age > 21 AND (city = 'NYC' OR city = 'LA')")
1496 .unwrap();
1497 assert!(stmt.where_clause.is_some());
1498 }
1499
1500 #[test]
1501 fn test_order_by_and_limit() {
1502 let stmt = parse_select("SELECT * FROM users ORDER BY created_at DESC, name ASC LIMIT 10")
1503 .unwrap();
1504 assert!(stmt.order_by.is_some());
1505 assert!(stmt.limit.is_some());
1506
1507 if let Some(limit) = stmt.limit {
1508 assert_eq!(limit.count, 10);
1509 }
1510 }
1511
1512 #[test]
1515 fn test_per_partition_limit_basic() {
1516 let stmt = parse_select("SELECT * FROM ks.t PER PARTITION LIMIT 2").unwrap();
1517 assert_eq!(stmt.per_partition_limit, Some(2));
1518 assert!(stmt.limit.is_none());
1519 }
1520
1521 #[test]
1522 fn test_per_partition_limit_with_global_limit() {
1523 let stmt = parse_select("SELECT * FROM ks.t PER PARTITION LIMIT 2 LIMIT 5").unwrap();
1524 assert_eq!(stmt.per_partition_limit, Some(2));
1525 assert_eq!(stmt.limit.map(|l| l.count), Some(5));
1526 }
1527
1528 #[test]
1529 fn test_per_partition_limit_after_order_by() {
1530 let stmt = parse_select("SELECT * FROM ks.t ORDER BY c DESC PER PARTITION LIMIT 3 LIMIT 9")
1531 .unwrap();
1532 assert!(stmt.order_by.is_some());
1533 assert_eq!(stmt.per_partition_limit, Some(3));
1534 assert_eq!(stmt.limit.map(|l| l.count), Some(9));
1535 }
1536
1537 #[test]
1538 fn test_per_partition_limit_rejects_zero() {
1539 assert!(parse_select("SELECT * FROM ks.t PER PARTITION LIMIT 0").is_err());
1540 }
1541
1542 #[test]
1543 fn test_global_limit_zero_is_accepted() {
1544 let stmt = parse_select("SELECT * FROM ks.t LIMIT 0").unwrap();
1547 assert_eq!(stmt.limit.map(|l| l.count), Some(0));
1548 }
1549
1550 #[test]
1551 fn test_per_partition_limit_rejects_negative() {
1552 assert!(parse_select("SELECT * FROM ks.t PER PARTITION LIMIT -1").is_err());
1553 }
1554
1555 #[test]
1556 fn test_per_partition_limit_rejects_after_global_limit() {
1557 assert!(parse_select("SELECT * FROM ks.t LIMIT 5 PER PARTITION LIMIT 2").is_err());
1558 }
1559
1560 #[test]
1561 fn test_per_partition_limit_rejects_after_limit_offset() {
1562 assert!(parse_select("SELECT * FROM ks.t LIMIT 5 OFFSET 1 PER PARTITION LIMIT 2").is_err());
1566 }
1567
1568 fn where_equal_literal(cql: &str) -> Value {
1572 let stmt = parse_select(cql).expect("statement must parse");
1573 let where_expr = stmt.where_clause.expect("WHERE clause expected");
1574 match where_expr {
1575 WhereExpression::Comparison(ComparisonExpression {
1576 operator: ComparisonOperator::Equal,
1577 right: ComparisonRightSide::Value(SelectExpression::Literal(v)),
1578 ..
1579 }) => v,
1580 other => panic!("Expected an `= literal` comparison, got: {:?}", other),
1581 }
1582 }
1583
1584 #[test]
1585 fn test_unquoted_uuid_literal() {
1586 let v = where_equal_literal(
1587 "SELECT * FROM ks.tbl WHERE id = 550e8400-e29b-41d4-a716-446655440000",
1588 );
1589 assert_eq!(
1590 v,
1591 Value::Uuid([
1592 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
1593 0x00, 0x00,
1594 ])
1595 );
1596 }
1597
1598 #[test]
1599 fn test_unquoted_uuid_literal_uppercase() {
1600 let lower = where_equal_literal(
1602 "SELECT * FROM ks.tbl WHERE id = 550e8400-e29b-41d4-a716-446655440000",
1603 );
1604 let upper = where_equal_literal(
1605 "SELECT * FROM ks.tbl WHERE id = 550E8400-E29B-41D4-A716-446655440000",
1606 );
1607 assert_eq!(lower, upper);
1608 }
1609
1610 #[test]
1611 fn test_uuid_starting_with_letter() {
1612 let v = where_equal_literal(
1615 "SELECT * FROM ks.tbl WHERE id = abcdef01-2345-6789-abcd-ef0123456789",
1616 );
1617 assert!(matches!(v, Value::Uuid(_)), "got {:?}", v);
1618 }
1619
1620 #[test]
1621 fn test_uuid_does_not_break_integer_literal() {
1622 let v = where_equal_literal("SELECT * FROM ks.tbl WHERE age = 5");
1625 assert_eq!(v, Value::BigInt(5));
1626 }
1627
1628 #[test]
1629 fn test_uuid_does_not_break_identifier_or_minus() {
1630 let stmt = parse_select("SELECT a716 - 1 FROM ks.tbl").expect("must parse");
1633 assert!(matches!(stmt.select_clause, SelectClause::Columns(_)));
1634 }
1635
1636 #[test]
1637 fn test_almost_uuid_too_long_is_not_uuid() {
1638 let result =
1642 parse_select("SELECT * FROM ks.tbl WHERE id = 550e8400-e29b-41d4-a716-4466554400000");
1643 if let Ok(stmt) = result {
1646 if let Some(WhereExpression::Comparison(c)) = stmt.where_clause {
1647 if let ComparisonRightSide::Value(SelectExpression::Literal(v)) = c.right {
1648 assert!(
1649 !matches!(v, Value::Uuid(_)),
1650 "33-hex-digit tail must not parse as a UUID, got {:?}",
1651 v
1652 );
1653 }
1654 }
1655 }
1656 }
1657
1658 #[test]
1659 fn test_blob_hex_literal() {
1660 let v = where_equal_literal("SELECT * FROM ks.tbl WHERE data = 0xdeadbeef");
1661 assert_eq!(v, Value::Blob(vec![0xde, 0xad, 0xbe, 0xef]));
1662 }
1663
1664 #[test]
1665 fn test_blob_hex_literal_uppercase_prefix_and_digits() {
1666 let v = where_equal_literal("SELECT * FROM ks.tbl WHERE data = 0XDEADBEEF");
1667 assert_eq!(v, Value::Blob(vec![0xde, 0xad, 0xbe, 0xef]));
1668 }
1669
1670 #[test]
1671 fn test_empty_blob_literal() {
1672 let v = where_equal_literal("SELECT * FROM ks.tbl WHERE data = 0x");
1673 assert_eq!(v, Value::Blob(vec![]));
1674 }
1675
1676 #[test]
1677 fn test_blob_odd_digit_count_errors() {
1678 assert!(parse_select("SELECT * FROM ks.tbl WHERE data = 0xabc").is_err());
1680 }
1681
1682 #[test]
1683 fn test_zero_integer_still_parses() {
1684 let v = where_equal_literal("SELECT * FROM ks.tbl WHERE age = 0");
1686 assert_eq!(v, Value::BigInt(0));
1687 }
1688
1689 #[test]
1692 fn test_single_bind_marker_in_where() {
1693 let stmt = parse_select("SELECT * FROM ks.tbl WHERE id = ?").expect("must parse");
1694 assert_eq!(stmt.bind_marker_count(), 1);
1695 let where_expr = stmt.where_clause.expect("WHERE expected");
1696 match where_expr {
1697 WhereExpression::Comparison(ComparisonExpression {
1698 operator: ComparisonOperator::Equal,
1699 right: ComparisonRightSide::Value(SelectExpression::BindMarker(idx)),
1700 ..
1701 }) => assert_eq!(idx, 0),
1702 other => panic!("expected `= ?` comparison with BindMarker(0), got {other:?}"),
1703 }
1704 }
1705
1706 #[test]
1707 fn test_multiple_bind_markers_numbered_left_to_right() {
1708 let stmt = parse_select("SELECT * FROM ks.tbl WHERE a = ? AND b = ?").expect("must parse");
1709 assert_eq!(stmt.bind_marker_count(), 2);
1710 let WhereExpression::And(exprs) = stmt.where_clause.expect("WHERE expected") else {
1712 panic!("expected AND of two comparisons");
1713 };
1714 let indices: Vec<usize> = exprs
1715 .iter()
1716 .map(|e| match e {
1717 WhereExpression::Comparison(ComparisonExpression {
1718 right: ComparisonRightSide::Value(SelectExpression::BindMarker(idx)),
1719 ..
1720 }) => *idx,
1721 other => panic!("expected BindMarker comparison, got {other:?}"),
1722 })
1723 .collect();
1724 assert_eq!(indices, vec![0, 1]);
1725 }
1726
1727 #[test]
1728 fn test_bind_marker_binds_to_literal() {
1729 let mut stmt = parse_select("SELECT * FROM ks.tbl WHERE id = ?").expect("must parse");
1730 stmt.bind_parameters(&[Value::Integer(42)])
1731 .expect("binding must succeed");
1732 let where_expr = stmt.where_clause.expect("WHERE expected");
1733 match where_expr {
1734 WhereExpression::Comparison(ComparisonExpression {
1735 right: ComparisonRightSide::Value(SelectExpression::Literal(v)),
1736 ..
1737 }) => assert_eq!(v, Value::Integer(42)),
1738 other => panic!("expected bound literal, got {other:?}"),
1739 }
1740 }
1741
1742 #[test]
1743 fn test_bind_marker_in_list_count() {
1744 let stmt = parse_select("SELECT * FROM ks.tbl WHERE id IN (?, ?, ?)").expect("must parse");
1745 assert_eq!(stmt.bind_marker_count(), 3);
1746 }
1747
1748 #[test]
1749 fn test_bind_marker_count_mismatch_errors() {
1750 let mut stmt = parse_select("SELECT * FROM ks.tbl WHERE id = ?").expect("must parse");
1751 assert!(stmt.bind_parameters(&[]).is_err(), "too few must error");
1752 let mut stmt2 = parse_select("SELECT * FROM ks.tbl WHERE id = ?").expect("must parse");
1753 assert!(
1754 stmt2
1755 .bind_parameters(&[Value::Integer(1), Value::Integer(2)])
1756 .is_err(),
1757 "too many must error"
1758 );
1759 }
1760
1761 #[test]
1762 fn test_no_bind_markers_count_zero() {
1763 let stmt = parse_select("SELECT * FROM ks.tbl WHERE id = 5").expect("must parse");
1764 assert_eq!(stmt.bind_marker_count(), 0);
1765 }
1766
1767 #[test]
1768 fn test_in_clause() {
1769 let stmt =
1770 parse_select("SELECT * FROM users WHERE status IN ('active', 'pending', 'suspended')")
1771 .unwrap();
1772 assert!(stmt.where_clause.is_some());
1773 }
1774
1775 #[test]
1776 fn test_between_clause() {
1777 let stmt = parse_select(
1778 "SELECT * FROM events WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31'",
1779 )
1780 .unwrap();
1781 assert!(stmt.where_clause.is_some());
1782 }
1783}