1use crate::{Error, Result, TableId, Value};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct SelectStatement {
13 pub select_clause: SelectClause,
15 pub from_clause: Option<FromClause>,
17 pub where_clause: Option<WhereExpression>,
19 pub group_by: Option<GroupByClause>,
21 pub having_clause: Option<WhereExpression>,
23 pub order_by: Option<OrderByClause>,
25 pub limit: Option<LimitClause>,
27 pub per_partition_limit: Option<u64>,
30 pub offset: Option<u64>,
32 pub allow_filtering: bool,
34}
35
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
38pub enum SelectClause {
39 All,
41 Columns(Vec<SelectExpression>),
43 Distinct(Vec<SelectExpression>),
45}
46
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49pub enum SelectExpression {
50 Column(ColumnRef),
52 Aggregate(AggregateFunction),
54 Function(FunctionCall),
56 WriteTimeTtl(WriteTimeTtlCall),
61 Literal(Value),
63 BindMarker(usize),
73 CollectionAccess(CollectionAccessExpression),
75 Arithmetic(ArithmeticExpression),
77 Aliased(Box<SelectExpression>, String),
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83pub struct ColumnRef {
84 pub table: Option<String>,
86 pub column: String,
88}
89
90#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
92pub struct AggregateFunction {
93 pub function: AggregateType,
95 pub args: Vec<SelectExpression>,
97 pub distinct: bool,
99}
100
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub enum AggregateType {
104 Count,
105 Sum,
106 Avg,
107 Min,
108 Max,
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
113pub struct FunctionCall {
114 pub name: String,
116 pub args: Vec<SelectExpression>,
118}
119
120#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
131pub enum WriteTimeTtlFunction {
132 WriteTime,
134 Ttl,
136}
137
138#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
140pub struct WriteTimeTtlCall {
141 pub function: WriteTimeTtlFunction,
143 pub column: String,
145 pub alias: Option<String>,
147}
148
149#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
151pub enum CollectionAccessExpression {
152 ListIndex(ColumnRef, Box<SelectExpression>),
154 MapKey(ColumnRef, Box<SelectExpression>),
156 SetContains(ColumnRef, Box<SelectExpression>),
158}
159
160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162pub struct ArithmeticExpression {
163 pub left: Box<SelectExpression>,
165 pub operator: ArithmeticOperator,
167 pub right: Box<SelectExpression>,
169}
170
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173pub enum ArithmeticOperator {
174 Add,
175 Subtract,
176 Multiply,
177 Divide,
178 Modulo,
179}
180
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub enum FromClause {
184 Table(TableId),
186 TableAlias(TableId, String),
188}
189
190#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
192#[allow(clippy::large_enum_variant)]
193pub enum WhereExpression {
194 Comparison(ComparisonExpression),
196 And(Vec<WhereExpression>),
198 Or(Vec<WhereExpression>),
200 Not(Box<WhereExpression>),
202 Parentheses(Box<WhereExpression>),
204}
205
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
208pub struct ComparisonExpression {
209 pub left: SelectExpression,
211 pub operator: ComparisonOperator,
213 pub right: ComparisonRightSide,
215}
216
217#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
219pub enum ComparisonRightSide {
220 Value(SelectExpression),
222 ValueList(Vec<SelectExpression>),
224 Range(SelectExpression, SelectExpression),
226}
227
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230pub enum ComparisonOperator {
231 Equal,
233 NotEqual,
235 LessThan,
237 LessThanOrEqual,
239 GreaterThan,
241 GreaterThanOrEqual,
243 In,
245 NotIn,
247 Like,
249 NotLike,
251 Between,
253 NotBetween,
255 IsNull,
257 IsNotNull,
259 Regex,
261 Contains,
263 ContainsKey,
265}
266
267#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269pub struct GroupByClause {
270 pub columns: Vec<ColumnRef>,
272}
273
274#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
276pub struct OrderByClause {
277 pub items: Vec<OrderByItem>,
279}
280
281#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
283pub struct OrderByItem {
284 pub expression: SelectExpression,
286 pub direction: SortDirection,
288}
289
290#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
292pub enum SortDirection {
293 Ascending,
294 Descending,
295}
296
297#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
299pub struct LimitClause {
300 pub count: u64,
302}
303
304impl SelectStatement {
305 pub fn select_all_from(table: TableId) -> Self {
307 Self {
308 select_clause: SelectClause::All,
309 from_clause: Some(FromClause::Table(table)),
310 where_clause: None,
311 group_by: None,
312 having_clause: None,
313 order_by: None,
314 limit: None,
315 per_partition_limit: None,
316 offset: None,
317 allow_filtering: false,
318 }
319 }
320
321 pub fn requires_aggregation(&self) -> bool {
323 self.group_by.is_some() || self.has_aggregate_functions()
324 }
325
326 pub fn has_aggregate_functions(&self) -> bool {
328 match &self.select_clause {
329 SelectClause::Columns(exprs) | SelectClause::Distinct(exprs) => {
330 exprs.iter().any(|expr| expr.is_aggregate())
331 }
332 SelectClause::All => false,
333 }
334 }
335
336 pub fn bind_marker_count(&self) -> usize {
343 let mut max_plus_one = 0usize;
344 if let SelectClause::Columns(exprs) | SelectClause::Distinct(exprs) = &self.select_clause {
345 for expr in exprs {
346 expr.scan_bind_markers(&mut max_plus_one);
347 }
348 }
349 if let Some(where_expr) = &self.where_clause {
350 where_expr.scan_bind_markers(&mut max_plus_one);
351 }
352 if let Some(having) = &self.having_clause {
353 having.scan_bind_markers(&mut max_plus_one);
354 }
355 max_plus_one
356 }
357
358 pub fn bind_parameters(&mut self, params: &[Value]) -> Result<()> {
367 let expected = self.bind_marker_count();
368 if params.len() != expected {
369 return Err(Error::query_execution(format!(
370 "Parameter count mismatch: query has {expected} bind marker(s), got {} parameter(s)",
371 params.len()
372 )));
373 }
374 if let SelectClause::Columns(exprs) | SelectClause::Distinct(exprs) =
375 &mut self.select_clause
376 {
377 for expr in exprs.iter_mut() {
378 expr.bind_parameters(params)?;
379 }
380 }
381 if let Some(where_expr) = &mut self.where_clause {
382 where_expr.bind_parameters(params)?;
383 }
384 if let Some(having) = &mut self.having_clause {
385 having.bind_parameters(params)?;
386 }
387 Ok(())
388 }
389
390 pub fn get_referenced_columns(&self) -> Vec<ColumnRef> {
395 let mut columns = Vec::new();
396
397 if let SelectClause::Columns(exprs) | SelectClause::Distinct(exprs) = &self.select_clause {
398 for expr in exprs {
399 columns.extend(expr.get_column_refs());
400 }
401 }
402
403 if let Some(where_expr) = &self.where_clause {
404 columns.extend(where_expr.get_column_refs());
405 }
406
407 if let Some(group_by) = &self.group_by {
408 columns.extend(group_by.columns.iter().cloned());
409 }
410
411 if let Some(having) = &self.having_clause {
412 columns.extend(having.get_column_refs());
413 }
414
415 if let Some(order_by) = &self.order_by {
416 for item in &order_by.items {
417 columns.extend(item.expression.get_column_refs());
418 }
419 }
420
421 columns
422 }
423}
424
425impl SelectExpression {
426 pub fn is_aggregate(&self) -> bool {
432 match self {
433 SelectExpression::Aggregate(_) => true,
434 SelectExpression::Aliased(inner, _) => inner.is_aggregate(),
435 _ => false,
436 }
437 }
438
439 fn scan_bind_markers(&self, max_plus_one: &mut usize) {
442 match self {
443 SelectExpression::BindMarker(idx) => *max_plus_one = (*max_plus_one).max(idx + 1),
444 SelectExpression::Aggregate(agg) => {
445 for arg in &agg.args {
446 arg.scan_bind_markers(max_plus_one);
447 }
448 }
449 SelectExpression::Function(func) => {
450 for arg in &func.args {
451 arg.scan_bind_markers(max_plus_one);
452 }
453 }
454 SelectExpression::CollectionAccess(access) => {
455 let (_, sub) = match access {
456 CollectionAccessExpression::ListIndex(c, e)
457 | CollectionAccessExpression::MapKey(c, e)
458 | CollectionAccessExpression::SetContains(c, e) => (c, e),
459 };
460 sub.scan_bind_markers(max_plus_one);
461 }
462 SelectExpression::Arithmetic(arith) => {
463 arith.left.scan_bind_markers(max_plus_one);
464 arith.right.scan_bind_markers(max_plus_one);
465 }
466 SelectExpression::Aliased(expr, _) => expr.scan_bind_markers(max_plus_one),
467 SelectExpression::Column(_)
468 | SelectExpression::Literal(_)
469 | SelectExpression::WriteTimeTtl(_) => {}
470 }
471 }
472
473 fn bind_parameters(&mut self, params: &[Value]) -> Result<()> {
477 match self {
478 SelectExpression::BindMarker(idx) => {
479 let value = params.get(*idx).ok_or_else(|| {
480 Error::query_execution(format!(
481 "Bind marker index {idx} has no corresponding parameter"
482 ))
483 })?;
484 *self = SelectExpression::Literal(value.clone());
485 }
486 SelectExpression::Aggregate(agg) => {
487 for arg in agg.args.iter_mut() {
488 arg.bind_parameters(params)?;
489 }
490 }
491 SelectExpression::Function(func) => {
492 for arg in func.args.iter_mut() {
493 arg.bind_parameters(params)?;
494 }
495 }
496 SelectExpression::CollectionAccess(access) => {
497 let sub = match access {
498 CollectionAccessExpression::ListIndex(_, e)
499 | CollectionAccessExpression::MapKey(_, e)
500 | CollectionAccessExpression::SetContains(_, e) => e,
501 };
502 sub.bind_parameters(params)?;
503 }
504 SelectExpression::Arithmetic(arith) => {
505 arith.left.bind_parameters(params)?;
506 arith.right.bind_parameters(params)?;
507 }
508 SelectExpression::Aliased(expr, _) => expr.bind_parameters(params)?,
509 SelectExpression::Column(_)
510 | SelectExpression::Literal(_)
511 | SelectExpression::WriteTimeTtl(_) => {}
512 }
513 Ok(())
514 }
515
516 pub fn get_column_refs(&self) -> Vec<ColumnRef> {
518 match self {
519 SelectExpression::Column(col_ref) => vec![col_ref.clone()],
520 SelectExpression::Aggregate(agg) => collect_refs(&agg.args),
521 SelectExpression::Function(func) => collect_refs(&func.args),
522 SelectExpression::WriteTimeTtl(call) => {
523 vec![ColumnRef::new(call.column.clone())]
524 }
525 SelectExpression::CollectionAccess(access) => {
526 let (col_ref, sub_expr) = match access {
527 CollectionAccessExpression::ListIndex(c, e)
528 | CollectionAccessExpression::MapKey(c, e)
529 | CollectionAccessExpression::SetContains(c, e) => (c, e),
530 };
531 let mut refs = vec![col_ref.clone()];
532 refs.extend(sub_expr.get_column_refs());
533 refs
534 }
535 SelectExpression::Arithmetic(arith) => {
536 let mut refs = arith.left.get_column_refs();
537 refs.extend(arith.right.get_column_refs());
538 refs
539 }
540 SelectExpression::Aliased(expr, _) => expr.get_column_refs(),
541 SelectExpression::Literal(_) | SelectExpression::BindMarker(_) => Vec::new(),
542 }
543 }
544}
545
546fn collect_refs(exprs: &[SelectExpression]) -> Vec<ColumnRef> {
548 exprs
549 .iter()
550 .flat_map(SelectExpression::get_column_refs)
551 .collect()
552}
553
554impl WhereExpression {
555 pub fn get_column_refs(&self) -> Vec<ColumnRef> {
557 match self {
558 WhereExpression::Comparison(comp) => {
559 let mut refs = comp.left.get_column_refs();
560 match &comp.right {
561 ComparisonRightSide::Value(expr) => {
562 refs.extend(expr.get_column_refs());
563 }
564 ComparisonRightSide::ValueList(exprs) => {
565 refs.extend(collect_refs(exprs));
566 }
567 ComparisonRightSide::Range(start, end) => {
568 refs.extend(start.get_column_refs());
569 refs.extend(end.get_column_refs());
570 }
571 }
572 refs
573 }
574 WhereExpression::And(exprs) | WhereExpression::Or(exprs) => exprs
575 .iter()
576 .flat_map(WhereExpression::get_column_refs)
577 .collect(),
578 WhereExpression::Not(expr) | WhereExpression::Parentheses(expr) => {
579 expr.get_column_refs()
580 }
581 }
582 }
583
584 fn scan_bind_markers(&self, max_plus_one: &mut usize) {
587 match self {
588 WhereExpression::Comparison(comp) => {
589 comp.left.scan_bind_markers(max_plus_one);
590 match &comp.right {
591 ComparisonRightSide::Value(expr) => expr.scan_bind_markers(max_plus_one),
592 ComparisonRightSide::ValueList(exprs) => {
593 for expr in exprs {
594 expr.scan_bind_markers(max_plus_one);
595 }
596 }
597 ComparisonRightSide::Range(start, end) => {
598 start.scan_bind_markers(max_plus_one);
599 end.scan_bind_markers(max_plus_one);
600 }
601 }
602 }
603 WhereExpression::And(exprs) | WhereExpression::Or(exprs) => {
604 for expr in exprs {
605 expr.scan_bind_markers(max_plus_one);
606 }
607 }
608 WhereExpression::Not(expr) | WhereExpression::Parentheses(expr) => {
609 expr.scan_bind_markers(max_plus_one)
610 }
611 }
612 }
613
614 fn bind_parameters(&mut self, params: &[Value]) -> Result<()> {
618 match self {
619 WhereExpression::Comparison(comp) => {
620 comp.left.bind_parameters(params)?;
621 match &mut comp.right {
622 ComparisonRightSide::Value(expr) => expr.bind_parameters(params)?,
623 ComparisonRightSide::ValueList(exprs) => {
624 for expr in exprs.iter_mut() {
625 expr.bind_parameters(params)?;
626 }
627 }
628 ComparisonRightSide::Range(start, end) => {
629 start.bind_parameters(params)?;
630 end.bind_parameters(params)?;
631 }
632 }
633 }
634 WhereExpression::And(exprs) | WhereExpression::Or(exprs) => {
635 for expr in exprs.iter_mut() {
636 expr.bind_parameters(params)?;
637 }
638 }
639 WhereExpression::Not(expr) | WhereExpression::Parentheses(expr) => {
640 expr.bind_parameters(params)?
641 }
642 }
643 Ok(())
644 }
645
646 pub fn can_pushdown_to_sstable(&self) -> bool {
651 match self {
652 WhereExpression::Comparison(comp) => {
653 matches!(comp.left, SelectExpression::Column(_))
654 && matches!(
655 comp.operator,
656 ComparisonOperator::Equal
657 | ComparisonOperator::LessThan
658 | ComparisonOperator::LessThanOrEqual
659 | ComparisonOperator::GreaterThan
660 | ComparisonOperator::GreaterThanOrEqual
661 | ComparisonOperator::In
662 | ComparisonOperator::Between
663 )
664 }
665 WhereExpression::And(exprs) => {
666 exprs.iter().all(WhereExpression::can_pushdown_to_sstable)
667 }
668 WhereExpression::Or(_) | WhereExpression::Not(_) => false,
669 WhereExpression::Parentheses(expr) => expr.can_pushdown_to_sstable(),
670 }
671 }
672}
673
674impl ColumnRef {
675 pub fn new(column: impl Into<String>) -> Self {
677 Self {
678 table: None,
679 column: column.into(),
680 }
681 }
682
683 pub fn qualified(table: impl Into<String>, column: impl Into<String>) -> Self {
685 Self {
686 table: Some(table.into()),
687 column: column.into(),
688 }
689 }
690}
691
692#[cfg(test)]
693mod tests {
694 use super::*;
695
696 #[test]
697 fn test_simple_select_statement() {
698 let stmt = SelectStatement::select_all_from(TableId::new("users"));
699 assert_eq!(stmt.select_clause, SelectClause::All);
700 assert!(!stmt.requires_aggregation());
701 }
702
703 #[test]
704 fn test_aggregate_detection() {
705 let stmt = SelectStatement {
706 select_clause: SelectClause::Columns(vec![SelectExpression::Aggregate(
707 AggregateFunction {
708 function: AggregateType::Count,
709 args: vec![SelectExpression::Column(ColumnRef::new("id"))],
710 distinct: false,
711 },
712 )]),
713 from_clause: Some(FromClause::Table(TableId::new("users"))),
714 where_clause: None,
715 group_by: None,
716 having_clause: None,
717 order_by: None,
718 limit: None,
719 per_partition_limit: None,
720 offset: None,
721 allow_filtering: false,
722 };
723
724 assert!(stmt.requires_aggregation());
725 assert!(stmt.has_aggregate_functions());
726 }
727
728 #[test]
729 fn test_column_references() {
730 let where_expr = WhereExpression::And(vec![
731 WhereExpression::Comparison(ComparisonExpression {
732 left: SelectExpression::Column(ColumnRef::new("age")),
733 operator: ComparisonOperator::GreaterThan,
734 right: ComparisonRightSide::Value(SelectExpression::Literal(Value::Integer(21))),
735 }),
736 WhereExpression::Comparison(ComparisonExpression {
737 left: SelectExpression::Column(ColumnRef::new("city")),
738 operator: ComparisonOperator::Equal,
739 right: ComparisonRightSide::Value(SelectExpression::Literal(Value::Text(
740 "NYC".to_string(),
741 ))),
742 }),
743 ]);
744
745 let column_refs = where_expr.get_column_refs();
746 assert_eq!(column_refs.len(), 2);
747 assert!(column_refs.iter().any(|col| col.column == "age"));
748 assert!(column_refs.iter().any(|col| col.column == "city"));
749 }
750
751 #[test]
752 fn test_pushdown_capability() {
753 let simple_comparison = WhereExpression::Comparison(ComparisonExpression {
754 left: SelectExpression::Column(ColumnRef::new("id")),
755 operator: ComparisonOperator::Equal,
756 right: ComparisonRightSide::Value(SelectExpression::Literal(Value::Integer(123))),
757 });
758
759 assert!(simple_comparison.can_pushdown_to_sstable());
760
761 let complex_or =
762 WhereExpression::Or(vec![simple_comparison.clone(), simple_comparison.clone()]);
763
764 assert!(!complex_or.can_pushdown_to_sstable());
765 }
766}