1use sqlparser::ast as sp;
4use sqlparser::dialect::GenericDialect;
5use sqlparser::parser::Parser;
6
7use crate::error::{Result, SqlError};
8use crate::types::{DataType, Value};
9
10#[derive(Debug, Clone)]
13pub enum Statement {
14 CreateTable(CreateTableStmt),
15 DropTable(DropTableStmt),
16 CreateIndex(CreateIndexStmt),
17 DropIndex(DropIndexStmt),
18 AlterTable(Box<AlterTableStmt>),
19 Insert(InsertStmt),
20 Select(Box<SelectQuery>),
21 Update(UpdateStmt),
22 Delete(DeleteStmt),
23 Begin,
24 Commit,
25 Rollback,
26 Explain(Box<Statement>),
27}
28
29#[derive(Debug, Clone)]
30pub struct AlterTableStmt {
31 pub table: String,
32 pub op: AlterTableOp,
33}
34
35#[derive(Debug, Clone)]
36pub enum AlterTableOp {
37 AddColumn {
38 column: Box<ColumnSpec>,
39 foreign_key: Option<ForeignKeyDef>,
40 if_not_exists: bool,
41 },
42 DropColumn {
43 name: String,
44 if_exists: bool,
45 },
46 RenameColumn {
47 old_name: String,
48 new_name: String,
49 },
50 RenameTable {
51 new_name: String,
52 },
53}
54
55#[derive(Debug, Clone)]
56pub struct CreateTableStmt {
57 pub name: String,
58 pub columns: Vec<ColumnSpec>,
59 pub primary_key: Vec<String>,
60 pub if_not_exists: bool,
61 pub check_constraints: Vec<TableCheckConstraint>,
62 pub foreign_keys: Vec<ForeignKeyDef>,
63}
64
65#[derive(Debug, Clone)]
66pub struct TableCheckConstraint {
67 pub name: Option<String>,
68 pub expr: Expr,
69 pub sql: String,
70}
71
72#[derive(Debug, Clone)]
73pub struct ForeignKeyDef {
74 pub name: Option<String>,
75 pub columns: Vec<String>,
76 pub foreign_table: String,
77 pub referred_columns: Vec<String>,
78}
79
80#[derive(Debug, Clone)]
81pub struct ColumnSpec {
82 pub name: String,
83 pub data_type: DataType,
84 pub nullable: bool,
85 pub is_primary_key: bool,
86 pub default_expr: Option<Expr>,
87 pub default_sql: Option<String>,
88 pub check_expr: Option<Expr>,
89 pub check_sql: Option<String>,
90 pub check_name: Option<String>,
91}
92
93#[derive(Debug, Clone)]
94pub struct DropTableStmt {
95 pub name: String,
96 pub if_exists: bool,
97}
98
99#[derive(Debug, Clone)]
100pub struct CreateIndexStmt {
101 pub index_name: String,
102 pub table_name: String,
103 pub columns: Vec<String>,
104 pub unique: bool,
105 pub if_not_exists: bool,
106}
107
108#[derive(Debug, Clone)]
109pub struct DropIndexStmt {
110 pub index_name: String,
111 pub if_exists: bool,
112}
113
114#[derive(Debug, Clone)]
115pub enum InsertSource {
116 Values(Vec<Vec<Expr>>),
117 Select(Box<SelectQuery>),
118}
119
120#[derive(Debug, Clone)]
121pub struct InsertStmt {
122 pub table: String,
123 pub columns: Vec<String>,
124 pub source: InsertSource,
125}
126
127#[derive(Debug, Clone)]
128pub struct TableRef {
129 pub name: String,
130 pub alias: Option<String>,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq)]
134pub enum JoinType {
135 Inner,
136 Cross,
137 Left,
138 Right,
139}
140
141#[derive(Debug, Clone)]
142pub struct JoinClause {
143 pub join_type: JoinType,
144 pub table: TableRef,
145 pub on_clause: Option<Expr>,
146}
147
148#[derive(Debug, Clone)]
149pub struct SelectStmt {
150 pub columns: Vec<SelectColumn>,
151 pub from: String,
152 pub from_alias: Option<String>,
153 pub joins: Vec<JoinClause>,
154 pub distinct: bool,
155 pub where_clause: Option<Expr>,
156 pub order_by: Vec<OrderByItem>,
157 pub limit: Option<Expr>,
158 pub offset: Option<Expr>,
159 pub group_by: Vec<Expr>,
160 pub having: Option<Expr>,
161}
162
163#[derive(Debug, Clone)]
164pub enum SetOp {
165 Union,
166 Intersect,
167 Except,
168}
169
170#[derive(Debug, Clone)]
171pub struct CompoundSelect {
172 pub op: SetOp,
173 pub all: bool,
174 pub left: Box<QueryBody>,
175 pub right: Box<QueryBody>,
176 pub order_by: Vec<OrderByItem>,
177 pub limit: Option<Expr>,
178 pub offset: Option<Expr>,
179}
180
181#[derive(Debug, Clone)]
182pub enum QueryBody {
183 Select(Box<SelectStmt>),
184 Compound(Box<CompoundSelect>),
185}
186
187#[derive(Debug, Clone)]
188pub struct CteDefinition {
189 pub name: String,
190 pub column_aliases: Vec<String>,
191 pub body: QueryBody,
192}
193
194#[derive(Debug, Clone)]
195pub struct SelectQuery {
196 pub ctes: Vec<CteDefinition>,
197 pub recursive: bool,
198 pub body: QueryBody,
199}
200
201#[derive(Debug, Clone)]
202pub struct UpdateStmt {
203 pub table: String,
204 pub assignments: Vec<(String, Expr)>,
205 pub where_clause: Option<Expr>,
206}
207
208#[derive(Debug, Clone)]
209pub struct DeleteStmt {
210 pub table: String,
211 pub where_clause: Option<Expr>,
212}
213
214#[derive(Debug, Clone)]
215pub enum SelectColumn {
216 AllColumns,
217 Expr { expr: Expr, alias: Option<String> },
218}
219
220#[derive(Debug, Clone)]
221pub struct OrderByItem {
222 pub expr: Expr,
223 pub descending: bool,
224 pub nulls_first: Option<bool>,
225}
226
227#[derive(Debug, Clone)]
228pub enum Expr {
229 Literal(Value),
230 Column(String),
231 QualifiedColumn {
232 table: String,
233 column: String,
234 },
235 BinaryOp {
236 left: Box<Expr>,
237 op: BinOp,
238 right: Box<Expr>,
239 },
240 UnaryOp {
241 op: UnaryOp,
242 expr: Box<Expr>,
243 },
244 IsNull(Box<Expr>),
245 IsNotNull(Box<Expr>),
246 Function {
247 name: String,
248 args: Vec<Expr>,
249 },
250 CountStar,
251 InSubquery {
252 expr: Box<Expr>,
253 subquery: Box<SelectStmt>,
254 negated: bool,
255 },
256 InList {
257 expr: Box<Expr>,
258 list: Vec<Expr>,
259 negated: bool,
260 },
261 Exists {
262 subquery: Box<SelectStmt>,
263 negated: bool,
264 },
265 ScalarSubquery(Box<SelectStmt>),
266 InSet {
267 expr: Box<Expr>,
268 values: std::collections::HashSet<Value>,
269 has_null: bool,
270 negated: bool,
271 },
272 Between {
273 expr: Box<Expr>,
274 low: Box<Expr>,
275 high: Box<Expr>,
276 negated: bool,
277 },
278 Like {
279 expr: Box<Expr>,
280 pattern: Box<Expr>,
281 escape: Option<Box<Expr>>,
282 negated: bool,
283 },
284 Case {
285 operand: Option<Box<Expr>>,
286 conditions: Vec<(Expr, Expr)>,
287 else_result: Option<Box<Expr>>,
288 },
289 Coalesce(Vec<Expr>),
290 Cast {
291 expr: Box<Expr>,
292 data_type: DataType,
293 },
294 Parameter(usize),
295 WindowFunction {
296 name: String,
297 args: Vec<Expr>,
298 spec: WindowSpec,
299 },
300}
301
302#[derive(Debug, Clone)]
303pub struct WindowSpec {
304 pub partition_by: Vec<Expr>,
305 pub order_by: Vec<OrderByItem>,
306 pub frame: Option<WindowFrame>,
307}
308
309#[derive(Debug, Clone)]
310pub struct WindowFrame {
311 pub units: WindowFrameUnits,
312 pub start: WindowFrameBound,
313 pub end: WindowFrameBound,
314}
315
316#[derive(Debug, Clone, Copy)]
317pub enum WindowFrameUnits {
318 Rows,
319 Range,
320 Groups,
321}
322
323#[derive(Debug, Clone)]
324pub enum WindowFrameBound {
325 UnboundedPreceding,
326 Preceding(Box<Expr>),
327 CurrentRow,
328 Following(Box<Expr>),
329 UnboundedFollowing,
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq)]
333pub enum BinOp {
334 Add,
335 Sub,
336 Mul,
337 Div,
338 Mod,
339 Eq,
340 NotEq,
341 Lt,
342 Gt,
343 LtEq,
344 GtEq,
345 And,
346 Or,
347 Concat,
348}
349
350#[derive(Debug, Clone, Copy, PartialEq, Eq)]
351pub enum UnaryOp {
352 Neg,
353 Not,
354}
355
356pub fn has_subquery(expr: &Expr) -> bool {
359 match expr {
360 Expr::InSubquery { .. } | Expr::Exists { .. } | Expr::ScalarSubquery(_) => true,
361 Expr::BinaryOp { left, right, .. } => has_subquery(left) || has_subquery(right),
362 Expr::UnaryOp { expr, .. } => has_subquery(expr),
363 Expr::IsNull(e) | Expr::IsNotNull(e) => has_subquery(e),
364 Expr::InList { expr, list, .. } => has_subquery(expr) || list.iter().any(has_subquery),
365 Expr::InSet { expr, .. } => has_subquery(expr),
366 Expr::Between {
367 expr, low, high, ..
368 } => has_subquery(expr) || has_subquery(low) || has_subquery(high),
369 Expr::Like {
370 expr,
371 pattern,
372 escape,
373 ..
374 } => {
375 has_subquery(expr)
376 || has_subquery(pattern)
377 || escape.as_ref().is_some_and(|e| has_subquery(e))
378 }
379 Expr::Case {
380 operand,
381 conditions,
382 else_result,
383 } => {
384 operand.as_ref().is_some_and(|e| has_subquery(e))
385 || conditions
386 .iter()
387 .any(|(c, r)| has_subquery(c) || has_subquery(r))
388 || else_result.as_ref().is_some_and(|e| has_subquery(e))
389 }
390 Expr::Coalesce(args) | Expr::Function { args, .. } => args.iter().any(has_subquery),
391 Expr::Cast { expr, .. } => has_subquery(expr),
392 _ => false,
393 }
394}
395
396pub fn parse_sql_expr(sql: &str) -> Result<Expr> {
399 let dialect = GenericDialect {};
400 let mut parser = Parser::new(&dialect)
401 .try_with_sql(sql)
402 .map_err(|e| SqlError::Parse(e.to_string()))?;
403 let sp_expr = parser
404 .parse_expr()
405 .map_err(|e| SqlError::Parse(e.to_string()))?;
406 convert_expr(&sp_expr)
407}
408
409pub fn parse_sql(sql: &str) -> Result<Statement> {
412 let dialect = GenericDialect {};
413 let stmts = Parser::parse_sql(&dialect, sql).map_err(|e| SqlError::Parse(e.to_string()))?;
414
415 if stmts.is_empty() {
416 return Err(SqlError::Parse("empty SQL".into()));
417 }
418 if stmts.len() > 1 {
419 return Err(SqlError::Unsupported("multiple statements".into()));
420 }
421
422 convert_statement(stmts.into_iter().next().unwrap())
423}
424
425pub fn count_params(stmt: &Statement) -> usize {
429 let mut max_idx = 0usize;
430 visit_exprs_stmt(stmt, &mut |e| {
431 if let Expr::Parameter(n) = e {
432 max_idx = max_idx.max(*n);
433 }
434 });
435 max_idx
436}
437
438pub fn bind_params(
440 stmt: &Statement,
441 params: &[crate::types::Value],
442) -> crate::error::Result<Statement> {
443 bind_stmt(stmt, params)
444}
445
446fn bind_stmt(stmt: &Statement, params: &[crate::types::Value]) -> crate::error::Result<Statement> {
447 match stmt {
448 Statement::Select(sq) => Ok(Statement::Select(Box::new(bind_select_query(sq, params)?))),
449 Statement::Insert(ins) => {
450 let source = match &ins.source {
451 InsertSource::Values(rows) => {
452 let bound = rows
453 .iter()
454 .map(|row| {
455 row.iter()
456 .map(|e| bind_expr(e, params))
457 .collect::<crate::error::Result<Vec<_>>>()
458 })
459 .collect::<crate::error::Result<Vec<_>>>()?;
460 InsertSource::Values(bound)
461 }
462 InsertSource::Select(sq) => {
463 InsertSource::Select(Box::new(bind_select_query(sq, params)?))
464 }
465 };
466 Ok(Statement::Insert(InsertStmt {
467 table: ins.table.clone(),
468 columns: ins.columns.clone(),
469 source,
470 }))
471 }
472 Statement::Update(upd) => {
473 let assignments = upd
474 .assignments
475 .iter()
476 .map(|(col, e)| Ok((col.clone(), bind_expr(e, params)?)))
477 .collect::<crate::error::Result<Vec<_>>>()?;
478 let where_clause = upd
479 .where_clause
480 .as_ref()
481 .map(|e| bind_expr(e, params))
482 .transpose()?;
483 Ok(Statement::Update(UpdateStmt {
484 table: upd.table.clone(),
485 assignments,
486 where_clause,
487 }))
488 }
489 Statement::Delete(del) => {
490 let where_clause = del
491 .where_clause
492 .as_ref()
493 .map(|e| bind_expr(e, params))
494 .transpose()?;
495 Ok(Statement::Delete(DeleteStmt {
496 table: del.table.clone(),
497 where_clause,
498 }))
499 }
500 Statement::Explain(inner) => Ok(Statement::Explain(Box::new(bind_stmt(inner, params)?))),
501 other => Ok(other.clone()),
502 }
503}
504
505fn bind_select(
506 sel: &SelectStmt,
507 params: &[crate::types::Value],
508) -> crate::error::Result<SelectStmt> {
509 let columns = sel
510 .columns
511 .iter()
512 .map(|c| match c {
513 SelectColumn::AllColumns => Ok(SelectColumn::AllColumns),
514 SelectColumn::Expr { expr, alias } => Ok(SelectColumn::Expr {
515 expr: bind_expr(expr, params)?,
516 alias: alias.clone(),
517 }),
518 })
519 .collect::<crate::error::Result<Vec<_>>>()?;
520 let joins = sel
521 .joins
522 .iter()
523 .map(|j| {
524 let on_clause = j
525 .on_clause
526 .as_ref()
527 .map(|e| bind_expr(e, params))
528 .transpose()?;
529 Ok(JoinClause {
530 join_type: j.join_type,
531 table: j.table.clone(),
532 on_clause,
533 })
534 })
535 .collect::<crate::error::Result<Vec<_>>>()?;
536 let where_clause = sel
537 .where_clause
538 .as_ref()
539 .map(|e| bind_expr(e, params))
540 .transpose()?;
541 let order_by = sel
542 .order_by
543 .iter()
544 .map(|o| {
545 Ok(OrderByItem {
546 expr: bind_expr(&o.expr, params)?,
547 descending: o.descending,
548 nulls_first: o.nulls_first,
549 })
550 })
551 .collect::<crate::error::Result<Vec<_>>>()?;
552 let limit = sel
553 .limit
554 .as_ref()
555 .map(|e| bind_expr(e, params))
556 .transpose()?;
557 let offset = sel
558 .offset
559 .as_ref()
560 .map(|e| bind_expr(e, params))
561 .transpose()?;
562 let group_by = sel
563 .group_by
564 .iter()
565 .map(|e| bind_expr(e, params))
566 .collect::<crate::error::Result<Vec<_>>>()?;
567 let having = sel
568 .having
569 .as_ref()
570 .map(|e| bind_expr(e, params))
571 .transpose()?;
572
573 Ok(SelectStmt {
574 columns,
575 from: sel.from.clone(),
576 from_alias: sel.from_alias.clone(),
577 joins,
578 distinct: sel.distinct,
579 where_clause,
580 order_by,
581 limit,
582 offset,
583 group_by,
584 having,
585 })
586}
587
588fn bind_query_body(
589 body: &QueryBody,
590 params: &[crate::types::Value],
591) -> crate::error::Result<QueryBody> {
592 match body {
593 QueryBody::Select(sel) => Ok(QueryBody::Select(Box::new(bind_select(sel, params)?))),
594 QueryBody::Compound(comp) => {
595 let order_by = comp
596 .order_by
597 .iter()
598 .map(|o| {
599 Ok(OrderByItem {
600 expr: bind_expr(&o.expr, params)?,
601 descending: o.descending,
602 nulls_first: o.nulls_first,
603 })
604 })
605 .collect::<crate::error::Result<Vec<_>>>()?;
606 let limit = comp
607 .limit
608 .as_ref()
609 .map(|e| bind_expr(e, params))
610 .transpose()?;
611 let offset = comp
612 .offset
613 .as_ref()
614 .map(|e| bind_expr(e, params))
615 .transpose()?;
616 Ok(QueryBody::Compound(Box::new(CompoundSelect {
617 op: comp.op.clone(),
618 all: comp.all,
619 left: Box::new(bind_query_body(&comp.left, params)?),
620 right: Box::new(bind_query_body(&comp.right, params)?),
621 order_by,
622 limit,
623 offset,
624 })))
625 }
626 }
627}
628
629fn bind_select_query(
630 sq: &SelectQuery,
631 params: &[crate::types::Value],
632) -> crate::error::Result<SelectQuery> {
633 let ctes = sq
634 .ctes
635 .iter()
636 .map(|cte| {
637 Ok(CteDefinition {
638 name: cte.name.clone(),
639 column_aliases: cte.column_aliases.clone(),
640 body: bind_query_body(&cte.body, params)?,
641 })
642 })
643 .collect::<crate::error::Result<Vec<_>>>()?;
644 let body = bind_query_body(&sq.body, params)?;
645 Ok(SelectQuery {
646 ctes,
647 recursive: sq.recursive,
648 body,
649 })
650}
651
652fn bind_expr(expr: &Expr, params: &[crate::types::Value]) -> crate::error::Result<Expr> {
653 match expr {
654 Expr::Parameter(n) => {
655 if *n == 0 || *n > params.len() {
656 return Err(SqlError::ParameterCountMismatch {
657 expected: *n,
658 got: params.len(),
659 });
660 }
661 Ok(Expr::Literal(params[*n - 1].clone()))
662 }
663 Expr::Literal(_) | Expr::Column(_) | Expr::QualifiedColumn { .. } | Expr::CountStar => {
664 Ok(expr.clone())
665 }
666 Expr::BinaryOp { left, op, right } => Ok(Expr::BinaryOp {
667 left: Box::new(bind_expr(left, params)?),
668 op: *op,
669 right: Box::new(bind_expr(right, params)?),
670 }),
671 Expr::UnaryOp { op, expr: e } => Ok(Expr::UnaryOp {
672 op: *op,
673 expr: Box::new(bind_expr(e, params)?),
674 }),
675 Expr::IsNull(e) => Ok(Expr::IsNull(Box::new(bind_expr(e, params)?))),
676 Expr::IsNotNull(e) => Ok(Expr::IsNotNull(Box::new(bind_expr(e, params)?))),
677 Expr::Function { name, args } => {
678 let args = args
679 .iter()
680 .map(|a| bind_expr(a, params))
681 .collect::<crate::error::Result<Vec<_>>>()?;
682 Ok(Expr::Function {
683 name: name.clone(),
684 args,
685 })
686 }
687 Expr::InSubquery {
688 expr: e,
689 subquery,
690 negated,
691 } => Ok(Expr::InSubquery {
692 expr: Box::new(bind_expr(e, params)?),
693 subquery: Box::new(bind_select(subquery, params)?),
694 negated: *negated,
695 }),
696 Expr::InList {
697 expr: e,
698 list,
699 negated,
700 } => {
701 let list = list
702 .iter()
703 .map(|l| bind_expr(l, params))
704 .collect::<crate::error::Result<Vec<_>>>()?;
705 Ok(Expr::InList {
706 expr: Box::new(bind_expr(e, params)?),
707 list,
708 negated: *negated,
709 })
710 }
711 Expr::Exists { subquery, negated } => Ok(Expr::Exists {
712 subquery: Box::new(bind_select(subquery, params)?),
713 negated: *negated,
714 }),
715 Expr::ScalarSubquery(sq) => Ok(Expr::ScalarSubquery(Box::new(bind_select(sq, params)?))),
716 Expr::InSet {
717 expr: e,
718 values,
719 has_null,
720 negated,
721 } => Ok(Expr::InSet {
722 expr: Box::new(bind_expr(e, params)?),
723 values: values.clone(),
724 has_null: *has_null,
725 negated: *negated,
726 }),
727 Expr::Between {
728 expr: e,
729 low,
730 high,
731 negated,
732 } => Ok(Expr::Between {
733 expr: Box::new(bind_expr(e, params)?),
734 low: Box::new(bind_expr(low, params)?),
735 high: Box::new(bind_expr(high, params)?),
736 negated: *negated,
737 }),
738 Expr::Like {
739 expr: e,
740 pattern,
741 escape,
742 negated,
743 } => Ok(Expr::Like {
744 expr: Box::new(bind_expr(e, params)?),
745 pattern: Box::new(bind_expr(pattern, params)?),
746 escape: escape
747 .as_ref()
748 .map(|esc| bind_expr(esc, params).map(Box::new))
749 .transpose()?,
750 negated: *negated,
751 }),
752 Expr::Case {
753 operand,
754 conditions,
755 else_result,
756 } => {
757 let operand = operand
758 .as_ref()
759 .map(|e| bind_expr(e, params).map(Box::new))
760 .transpose()?;
761 let conditions = conditions
762 .iter()
763 .map(|(cond, then)| Ok((bind_expr(cond, params)?, bind_expr(then, params)?)))
764 .collect::<crate::error::Result<Vec<_>>>()?;
765 let else_result = else_result
766 .as_ref()
767 .map(|e| bind_expr(e, params).map(Box::new))
768 .transpose()?;
769 Ok(Expr::Case {
770 operand,
771 conditions,
772 else_result,
773 })
774 }
775 Expr::Coalesce(args) => {
776 let args = args
777 .iter()
778 .map(|a| bind_expr(a, params))
779 .collect::<crate::error::Result<Vec<_>>>()?;
780 Ok(Expr::Coalesce(args))
781 }
782 Expr::Cast { expr: e, data_type } => Ok(Expr::Cast {
783 expr: Box::new(bind_expr(e, params)?),
784 data_type: *data_type,
785 }),
786 Expr::WindowFunction { name, args, spec } => {
787 let args = args
788 .iter()
789 .map(|a| bind_expr(a, params))
790 .collect::<crate::error::Result<Vec<_>>>()?;
791 let partition_by = spec
792 .partition_by
793 .iter()
794 .map(|e| bind_expr(e, params))
795 .collect::<crate::error::Result<Vec<_>>>()?;
796 let order_by = spec
797 .order_by
798 .iter()
799 .map(|o| {
800 Ok(OrderByItem {
801 expr: bind_expr(&o.expr, params)?,
802 descending: o.descending,
803 nulls_first: o.nulls_first,
804 })
805 })
806 .collect::<crate::error::Result<Vec<_>>>()?;
807 let frame = match &spec.frame {
808 Some(f) => Some(WindowFrame {
809 units: f.units,
810 start: bind_frame_bound(&f.start, params)?,
811 end: bind_frame_bound(&f.end, params)?,
812 }),
813 None => None,
814 };
815 Ok(Expr::WindowFunction {
816 name: name.clone(),
817 args,
818 spec: WindowSpec {
819 partition_by,
820 order_by,
821 frame,
822 },
823 })
824 }
825 }
826}
827
828fn bind_frame_bound(
829 bound: &WindowFrameBound,
830 params: &[crate::types::Value],
831) -> crate::error::Result<WindowFrameBound> {
832 match bound {
833 WindowFrameBound::Preceding(e) => {
834 Ok(WindowFrameBound::Preceding(Box::new(bind_expr(e, params)?)))
835 }
836 WindowFrameBound::Following(e) => {
837 Ok(WindowFrameBound::Following(Box::new(bind_expr(e, params)?)))
838 }
839 other => Ok(other.clone()),
840 }
841}
842
843fn visit_exprs_stmt(stmt: &Statement, visitor: &mut impl FnMut(&Expr)) {
844 match stmt {
845 Statement::Select(sq) => {
846 for cte in &sq.ctes {
847 visit_exprs_query_body(&cte.body, visitor);
848 }
849 visit_exprs_query_body(&sq.body, visitor);
850 }
851 Statement::Insert(ins) => match &ins.source {
852 InsertSource::Values(rows) => {
853 for row in rows {
854 for e in row {
855 visit_expr(e, visitor);
856 }
857 }
858 }
859 InsertSource::Select(sq) => {
860 for cte in &sq.ctes {
861 visit_exprs_query_body(&cte.body, visitor);
862 }
863 visit_exprs_query_body(&sq.body, visitor);
864 }
865 },
866 Statement::Update(upd) => {
867 for (_, e) in &upd.assignments {
868 visit_expr(e, visitor);
869 }
870 if let Some(w) = &upd.where_clause {
871 visit_expr(w, visitor);
872 }
873 }
874 Statement::Delete(del) => {
875 if let Some(w) = &del.where_clause {
876 visit_expr(w, visitor);
877 }
878 }
879 Statement::Explain(inner) => visit_exprs_stmt(inner, visitor),
880 _ => {}
881 }
882}
883
884fn visit_exprs_query_body(body: &QueryBody, visitor: &mut impl FnMut(&Expr)) {
885 match body {
886 QueryBody::Select(sel) => visit_exprs_select(sel, visitor),
887 QueryBody::Compound(comp) => {
888 visit_exprs_query_body(&comp.left, visitor);
889 visit_exprs_query_body(&comp.right, visitor);
890 for o in &comp.order_by {
891 visit_expr(&o.expr, visitor);
892 }
893 if let Some(l) = &comp.limit {
894 visit_expr(l, visitor);
895 }
896 if let Some(o) = &comp.offset {
897 visit_expr(o, visitor);
898 }
899 }
900 }
901}
902
903fn visit_exprs_select(sel: &SelectStmt, visitor: &mut impl FnMut(&Expr)) {
904 for col in &sel.columns {
905 if let SelectColumn::Expr { expr, .. } = col {
906 visit_expr(expr, visitor);
907 }
908 }
909 for j in &sel.joins {
910 if let Some(on) = &j.on_clause {
911 visit_expr(on, visitor);
912 }
913 }
914 if let Some(w) = &sel.where_clause {
915 visit_expr(w, visitor);
916 }
917 for o in &sel.order_by {
918 visit_expr(&o.expr, visitor);
919 }
920 if let Some(l) = &sel.limit {
921 visit_expr(l, visitor);
922 }
923 if let Some(o) = &sel.offset {
924 visit_expr(o, visitor);
925 }
926 for g in &sel.group_by {
927 visit_expr(g, visitor);
928 }
929 if let Some(h) = &sel.having {
930 visit_expr(h, visitor);
931 }
932}
933
934fn visit_expr(expr: &Expr, visitor: &mut impl FnMut(&Expr)) {
935 visitor(expr);
936 match expr {
937 Expr::BinaryOp { left, right, .. } => {
938 visit_expr(left, visitor);
939 visit_expr(right, visitor);
940 }
941 Expr::UnaryOp { expr: e, .. } | Expr::IsNull(e) | Expr::IsNotNull(e) => {
942 visit_expr(e, visitor);
943 }
944 Expr::Function { args, .. } | Expr::Coalesce(args) => {
945 for a in args {
946 visit_expr(a, visitor);
947 }
948 }
949 Expr::InSubquery {
950 expr: e, subquery, ..
951 } => {
952 visit_expr(e, visitor);
953 visit_exprs_select(subquery, visitor);
954 }
955 Expr::InList { expr: e, list, .. } => {
956 visit_expr(e, visitor);
957 for l in list {
958 visit_expr(l, visitor);
959 }
960 }
961 Expr::Exists { subquery, .. } => visit_exprs_select(subquery, visitor),
962 Expr::ScalarSubquery(sq) => visit_exprs_select(sq, visitor),
963 Expr::InSet { expr: e, .. } => visit_expr(e, visitor),
964 Expr::Between {
965 expr: e, low, high, ..
966 } => {
967 visit_expr(e, visitor);
968 visit_expr(low, visitor);
969 visit_expr(high, visitor);
970 }
971 Expr::Like {
972 expr: e,
973 pattern,
974 escape,
975 ..
976 } => {
977 visit_expr(e, visitor);
978 visit_expr(pattern, visitor);
979 if let Some(esc) = escape {
980 visit_expr(esc, visitor);
981 }
982 }
983 Expr::Case {
984 operand,
985 conditions,
986 else_result,
987 } => {
988 if let Some(op) = operand {
989 visit_expr(op, visitor);
990 }
991 for (cond, then) in conditions {
992 visit_expr(cond, visitor);
993 visit_expr(then, visitor);
994 }
995 if let Some(el) = else_result {
996 visit_expr(el, visitor);
997 }
998 }
999 Expr::Cast { expr: e, .. } => visit_expr(e, visitor),
1000 Expr::WindowFunction { args, spec, .. } => {
1001 for a in args {
1002 visit_expr(a, visitor);
1003 }
1004 for p in &spec.partition_by {
1005 visit_expr(p, visitor);
1006 }
1007 for o in &spec.order_by {
1008 visit_expr(&o.expr, visitor);
1009 }
1010 if let Some(ref frame) = spec.frame {
1011 if let WindowFrameBound::Preceding(e) | WindowFrameBound::Following(e) =
1012 &frame.start
1013 {
1014 visit_expr(e, visitor);
1015 }
1016 if let WindowFrameBound::Preceding(e) | WindowFrameBound::Following(e) = &frame.end
1017 {
1018 visit_expr(e, visitor);
1019 }
1020 }
1021 }
1022 Expr::Literal(_)
1023 | Expr::Column(_)
1024 | Expr::QualifiedColumn { .. }
1025 | Expr::CountStar
1026 | Expr::Parameter(_) => {}
1027 }
1028}
1029
1030fn convert_statement(stmt: sp::Statement) -> Result<Statement> {
1033 match stmt {
1034 sp::Statement::CreateTable(ct) => convert_create_table(ct),
1035 sp::Statement::CreateIndex(ci) => convert_create_index(ci),
1036 sp::Statement::Drop {
1037 object_type: sp::ObjectType::Table,
1038 if_exists,
1039 names,
1040 ..
1041 } => {
1042 if names.len() != 1 {
1043 return Err(SqlError::Unsupported("multi-table DROP".into()));
1044 }
1045 Ok(Statement::DropTable(DropTableStmt {
1046 name: object_name_to_string(&names[0]),
1047 if_exists,
1048 }))
1049 }
1050 sp::Statement::Drop {
1051 object_type: sp::ObjectType::Index,
1052 if_exists,
1053 names,
1054 ..
1055 } => {
1056 if names.len() != 1 {
1057 return Err(SqlError::Unsupported("multi-index DROP".into()));
1058 }
1059 Ok(Statement::DropIndex(DropIndexStmt {
1060 index_name: object_name_to_string(&names[0]),
1061 if_exists,
1062 }))
1063 }
1064 sp::Statement::AlterTable(at) => convert_alter_table(at),
1065 sp::Statement::Insert(insert) => convert_insert(insert),
1066 sp::Statement::Query(query) => convert_query(*query),
1067 sp::Statement::Update(update) => convert_update(update),
1068 sp::Statement::Delete(delete) => convert_delete(delete),
1069 sp::Statement::StartTransaction { .. } => Ok(Statement::Begin),
1070 sp::Statement::Commit { .. } => Ok(Statement::Commit),
1071 sp::Statement::Rollback { .. } => Ok(Statement::Rollback),
1072 sp::Statement::Explain {
1073 statement, analyze, ..
1074 } => {
1075 if analyze {
1076 return Err(SqlError::Unsupported("EXPLAIN ANALYZE".into()));
1077 }
1078 let inner = convert_statement(*statement)?;
1079 Ok(Statement::Explain(Box::new(inner)))
1080 }
1081 _ => Err(SqlError::Unsupported(format!("statement type: {}", stmt))),
1082 }
1083}
1084
1085fn convert_column_def(
1088 col_def: &sp::ColumnDef,
1089) -> Result<(ColumnSpec, Option<ForeignKeyDef>, bool)> {
1090 let col_name = col_def.name.value.clone();
1091 let data_type = convert_data_type(&col_def.data_type)?;
1092 let mut nullable = true;
1093 let mut is_primary_key = false;
1094 let mut default_expr = None;
1095 let mut default_sql = None;
1096 let mut check_expr = None;
1097 let mut check_sql = None;
1098 let mut check_name = None;
1099 let mut fk_def = None;
1100
1101 for opt in &col_def.options {
1102 match &opt.option {
1103 sp::ColumnOption::NotNull => nullable = false,
1104 sp::ColumnOption::Null => nullable = true,
1105 sp::ColumnOption::PrimaryKey(_) => {
1106 is_primary_key = true;
1107 nullable = false;
1108 }
1109 sp::ColumnOption::Default(expr) => {
1110 default_sql = Some(expr.to_string());
1111 default_expr = Some(convert_expr(expr)?);
1112 }
1113 sp::ColumnOption::Check(check) => {
1114 check_sql = Some(check.expr.to_string());
1115 let converted = convert_expr(&check.expr)?;
1116 if has_subquery(&converted) {
1117 return Err(SqlError::Unsupported("subquery in CHECK constraint".into()));
1118 }
1119 check_expr = Some(converted);
1120 check_name = check.name.as_ref().map(|n| n.value.clone());
1121 }
1122 sp::ColumnOption::ForeignKey(fk) => {
1123 convert_fk_actions(&fk.on_delete, &fk.on_update)?;
1124 let ftable = object_name_to_string(&fk.foreign_table).to_ascii_lowercase();
1125 let referred: Vec<String> = fk
1126 .referred_columns
1127 .iter()
1128 .map(|i| i.value.to_ascii_lowercase())
1129 .collect();
1130 fk_def = Some(ForeignKeyDef {
1131 name: fk.name.as_ref().map(|n| n.value.clone()),
1132 columns: vec![col_name.to_ascii_lowercase()],
1133 foreign_table: ftable,
1134 referred_columns: referred,
1135 });
1136 }
1137 _ => {}
1138 }
1139 }
1140
1141 let spec = ColumnSpec {
1142 name: col_name,
1143 data_type,
1144 nullable,
1145 is_primary_key,
1146 default_expr,
1147 default_sql,
1148 check_expr,
1149 check_sql,
1150 check_name,
1151 };
1152 Ok((spec, fk_def, is_primary_key))
1153}
1154
1155fn convert_create_table(ct: sp::CreateTable) -> Result<Statement> {
1156 let name = object_name_to_string(&ct.name);
1157 let if_not_exists = ct.if_not_exists;
1158
1159 let mut columns = Vec::new();
1160 let mut inline_pk: Vec<String> = Vec::new();
1161 let mut foreign_keys: Vec<ForeignKeyDef> = Vec::new();
1162
1163 for col_def in &ct.columns {
1164 let (spec, fk_def, was_pk) = convert_column_def(col_def)?;
1165 if was_pk {
1166 inline_pk.push(spec.name.clone());
1167 }
1168 if let Some(fk) = fk_def {
1169 foreign_keys.push(fk);
1170 }
1171 columns.push(spec);
1172 }
1173
1174 let mut check_constraints: Vec<TableCheckConstraint> = Vec::new();
1176
1177 for constraint in &ct.constraints {
1178 match constraint {
1179 sp::TableConstraint::PrimaryKey(pk_constraint) => {
1180 for idx_col in &pk_constraint.columns {
1181 let col_name = match &idx_col.column.expr {
1182 sp::Expr::Identifier(ident) => ident.value.clone(),
1183 _ => continue,
1184 };
1185 if !inline_pk.contains(&col_name) {
1186 inline_pk.push(col_name.clone());
1187 }
1188 if let Some(col) = columns.iter_mut().find(|c| c.name == col_name) {
1189 col.nullable = false;
1190 col.is_primary_key = true;
1191 }
1192 }
1193 }
1194 sp::TableConstraint::Check(check) => {
1195 let sql = check.expr.to_string();
1196 let converted = convert_expr(&check.expr)?;
1197 if has_subquery(&converted) {
1198 return Err(SqlError::Unsupported("subquery in CHECK constraint".into()));
1199 }
1200 check_constraints.push(TableCheckConstraint {
1201 name: check.name.as_ref().map(|n| n.value.clone()),
1202 expr: converted,
1203 sql,
1204 });
1205 }
1206 sp::TableConstraint::ForeignKey(fk) => {
1207 convert_fk_actions(&fk.on_delete, &fk.on_update)?;
1208 let cols: Vec<String> = fk
1209 .columns
1210 .iter()
1211 .map(|i| i.value.to_ascii_lowercase())
1212 .collect();
1213 let ftable = object_name_to_string(&fk.foreign_table).to_ascii_lowercase();
1214 let referred: Vec<String> = fk
1215 .referred_columns
1216 .iter()
1217 .map(|i| i.value.to_ascii_lowercase())
1218 .collect();
1219 foreign_keys.push(ForeignKeyDef {
1220 name: fk.name.as_ref().map(|n| n.value.clone()),
1221 columns: cols,
1222 foreign_table: ftable,
1223 referred_columns: referred,
1224 });
1225 }
1226 _ => {}
1227 }
1228 }
1229
1230 Ok(Statement::CreateTable(CreateTableStmt {
1231 name,
1232 columns,
1233 primary_key: inline_pk,
1234 if_not_exists,
1235 check_constraints,
1236 foreign_keys,
1237 }))
1238}
1239
1240fn convert_alter_table(at: sp::AlterTable) -> Result<Statement> {
1241 let table = object_name_to_string(&at.name);
1242 if at.operations.len() != 1 {
1243 return Err(SqlError::Unsupported(
1244 "ALTER TABLE with multiple operations".into(),
1245 ));
1246 }
1247 let op = match at.operations.into_iter().next().unwrap() {
1248 sp::AlterTableOperation::AddColumn {
1249 column_def,
1250 if_not_exists,
1251 ..
1252 } => {
1253 let (spec, fk, _was_pk) = convert_column_def(&column_def)?;
1254 AlterTableOp::AddColumn {
1255 column: Box::new(spec),
1256 foreign_key: fk,
1257 if_not_exists,
1258 }
1259 }
1260 sp::AlterTableOperation::DropColumn {
1261 column_names,
1262 if_exists,
1263 ..
1264 } => {
1265 if column_names.len() != 1 {
1266 return Err(SqlError::Unsupported(
1267 "DROP COLUMN with multiple columns".into(),
1268 ));
1269 }
1270 AlterTableOp::DropColumn {
1271 name: column_names.into_iter().next().unwrap().value,
1272 if_exists,
1273 }
1274 }
1275 sp::AlterTableOperation::RenameColumn {
1276 old_column_name,
1277 new_column_name,
1278 } => AlterTableOp::RenameColumn {
1279 old_name: old_column_name.value,
1280 new_name: new_column_name.value,
1281 },
1282 sp::AlterTableOperation::RenameTable { table_name } => {
1283 let new_name = match table_name {
1284 sp::RenameTableNameKind::To(name) | sp::RenameTableNameKind::As(name) => {
1285 object_name_to_string(&name)
1286 }
1287 };
1288 AlterTableOp::RenameTable { new_name }
1289 }
1290 other => {
1291 return Err(SqlError::Unsupported(format!(
1292 "ALTER TABLE operation: {other}"
1293 )));
1294 }
1295 };
1296 Ok(Statement::AlterTable(Box::new(AlterTableStmt {
1297 table,
1298 op,
1299 })))
1300}
1301
1302fn convert_fk_actions(
1303 on_delete: &Option<sp::ReferentialAction>,
1304 on_update: &Option<sp::ReferentialAction>,
1305) -> Result<()> {
1306 for action in [on_delete, on_update] {
1307 match action {
1308 None
1309 | Some(sp::ReferentialAction::Restrict)
1310 | Some(sp::ReferentialAction::NoAction) => {}
1311 Some(other) => {
1312 return Err(SqlError::Unsupported(format!(
1313 "FOREIGN KEY action: {other}"
1314 )));
1315 }
1316 }
1317 }
1318 Ok(())
1319}
1320
1321fn convert_create_index(ci: sp::CreateIndex) -> Result<Statement> {
1322 let index_name = ci
1323 .name
1324 .as_ref()
1325 .map(object_name_to_string)
1326 .ok_or_else(|| SqlError::Parse("index name required".into()))?;
1327
1328 let table_name = object_name_to_string(&ci.table_name);
1329
1330 let columns: Vec<String> = ci
1331 .columns
1332 .iter()
1333 .map(|idx_col| match &idx_col.column.expr {
1334 sp::Expr::Identifier(ident) => Ok(ident.value.clone()),
1335 other => Err(SqlError::Unsupported(format!("expression index: {other}"))),
1336 })
1337 .collect::<Result<_>>()?;
1338
1339 if columns.is_empty() {
1340 return Err(SqlError::Parse(
1341 "index must have at least one column".into(),
1342 ));
1343 }
1344
1345 Ok(Statement::CreateIndex(CreateIndexStmt {
1346 index_name,
1347 table_name,
1348 columns,
1349 unique: ci.unique,
1350 if_not_exists: ci.if_not_exists,
1351 }))
1352}
1353
1354fn convert_insert(insert: sp::Insert) -> Result<Statement> {
1355 let table = match &insert.table {
1356 sp::TableObject::TableName(name) => object_name_to_string(name).to_ascii_lowercase(),
1357 _ => return Err(SqlError::Unsupported("INSERT into non-table object".into())),
1358 };
1359
1360 let columns: Vec<String> = insert
1361 .columns
1362 .iter()
1363 .map(|c| c.value.to_ascii_lowercase())
1364 .collect();
1365
1366 let query = insert
1367 .source
1368 .ok_or_else(|| SqlError::Parse("INSERT requires VALUES or SELECT".into()))?;
1369
1370 let source = match *query.body {
1371 sp::SetExpr::Values(sp::Values { rows, .. }) => {
1372 let mut result = Vec::new();
1373 for row in rows {
1374 let mut exprs = Vec::new();
1375 for expr in row {
1376 exprs.push(convert_expr(&expr)?);
1377 }
1378 result.push(exprs);
1379 }
1380 InsertSource::Values(result)
1381 }
1382 _ => {
1383 let (ctes, recursive) = if let Some(ref with) = query.with {
1384 convert_with(with)?
1385 } else {
1386 (vec![], false)
1387 };
1388 let body = convert_query_body(&query)?;
1389 InsertSource::Select(Box::new(SelectQuery {
1390 ctes,
1391 recursive,
1392 body,
1393 }))
1394 }
1395 };
1396
1397 Ok(Statement::Insert(InsertStmt {
1398 table,
1399 columns,
1400 source,
1401 }))
1402}
1403
1404fn convert_select_body(select: &sp::Select) -> Result<SelectStmt> {
1405 let distinct = match &select.distinct {
1406 Some(sp::Distinct::Distinct) => true,
1407 Some(sp::Distinct::On(_)) => {
1408 return Err(SqlError::Unsupported("DISTINCT ON".into()));
1409 }
1410 _ => false,
1411 };
1412
1413 let (from, from_alias, joins) = if select.from.is_empty() {
1415 (String::new(), None, vec![])
1416 } else if select.from.len() == 1 {
1417 let table_with_joins = &select.from[0];
1418 let (name, alias) = match &table_with_joins.relation {
1419 sp::TableFactor::Table { name, alias, .. } => {
1420 let table_name = object_name_to_string(name);
1421 let alias_str = alias.as_ref().map(|a| a.name.value.clone());
1422 (table_name, alias_str)
1423 }
1424 _ => return Err(SqlError::Unsupported("non-table FROM source".into())),
1425 };
1426 let j = table_with_joins
1427 .joins
1428 .iter()
1429 .map(convert_join)
1430 .collect::<Result<Vec<_>>>()?;
1431 (name, alias, j)
1432 } else {
1433 return Err(SqlError::Unsupported("comma-separated FROM tables".into()));
1434 };
1435
1436 let columns: Vec<SelectColumn> = select
1438 .projection
1439 .iter()
1440 .map(convert_select_item)
1441 .collect::<Result<_>>()?;
1442
1443 let where_clause = select.selection.as_ref().map(convert_expr).transpose()?;
1445
1446 let group_by = match &select.group_by {
1448 sp::GroupByExpr::Expressions(exprs, _) => {
1449 exprs.iter().map(convert_expr).collect::<Result<_>>()?
1450 }
1451 sp::GroupByExpr::All(_) => {
1452 return Err(SqlError::Unsupported("GROUP BY ALL".into()));
1453 }
1454 };
1455
1456 let having = select.having.as_ref().map(convert_expr).transpose()?;
1458
1459 Ok(SelectStmt {
1460 columns,
1461 from,
1462 from_alias,
1463 joins,
1464 distinct,
1465 where_clause,
1466 order_by: vec![],
1467 limit: None,
1468 offset: None,
1469 group_by,
1470 having,
1471 })
1472}
1473
1474fn convert_set_expr(set_expr: &sp::SetExpr) -> Result<QueryBody> {
1475 match set_expr {
1476 sp::SetExpr::Select(sel) => Ok(QueryBody::Select(Box::new(convert_select_body(sel)?))),
1477 sp::SetExpr::SetOperation {
1478 op,
1479 set_quantifier,
1480 left,
1481 right,
1482 } => {
1483 let set_op = match op {
1484 sp::SetOperator::Union => SetOp::Union,
1485 sp::SetOperator::Intersect => SetOp::Intersect,
1486 sp::SetOperator::Except | sp::SetOperator::Minus => SetOp::Except,
1487 };
1488 let all = match set_quantifier {
1489 sp::SetQuantifier::All => true,
1490 sp::SetQuantifier::None | sp::SetQuantifier::Distinct => false,
1491 _ => {
1492 return Err(SqlError::Unsupported("BY NAME set operations".into()));
1493 }
1494 };
1495 Ok(QueryBody::Compound(Box::new(CompoundSelect {
1496 op: set_op,
1497 all,
1498 left: Box::new(convert_set_expr(left)?),
1499 right: Box::new(convert_set_expr(right)?),
1500 order_by: vec![],
1501 limit: None,
1502 offset: None,
1503 })))
1504 }
1505 _ => Err(SqlError::Unsupported("unsupported set expression".into())),
1506 }
1507}
1508
1509fn convert_query_body(query: &sp::Query) -> Result<QueryBody> {
1510 let mut body = convert_set_expr(&query.body)?;
1511
1512 let order_by = if let Some(ref ob) = query.order_by {
1514 match &ob.kind {
1515 sp::OrderByKind::Expressions(exprs) => exprs
1516 .iter()
1517 .map(convert_order_by_expr)
1518 .collect::<Result<_>>()?,
1519 sp::OrderByKind::All { .. } => {
1520 return Err(SqlError::Unsupported("ORDER BY ALL".into()));
1521 }
1522 }
1523 } else {
1524 vec![]
1525 };
1526
1527 let (limit, offset) = match &query.limit_clause {
1529 Some(sp::LimitClause::LimitOffset { limit, offset, .. }) => {
1530 let l = limit.as_ref().map(convert_expr).transpose()?;
1531 let o = offset
1532 .as_ref()
1533 .map(|o| convert_expr(&o.value))
1534 .transpose()?;
1535 (l, o)
1536 }
1537 Some(sp::LimitClause::OffsetCommaLimit { limit, offset }) => {
1538 let l = Some(convert_expr(limit)?);
1539 let o = Some(convert_expr(offset)?);
1540 (l, o)
1541 }
1542 None => (None, None),
1543 };
1544
1545 match &mut body {
1546 QueryBody::Select(sel) => {
1547 sel.order_by = order_by;
1548 sel.limit = limit;
1549 sel.offset = offset;
1550 }
1551 QueryBody::Compound(comp) => {
1552 comp.order_by = order_by;
1553 comp.limit = limit;
1554 comp.offset = offset;
1555 }
1556 }
1557
1558 Ok(body)
1559}
1560
1561fn convert_subquery(query: &sp::Query) -> Result<SelectStmt> {
1562 if query.with.is_some() {
1563 return Err(SqlError::Unsupported("CTEs in subqueries".into()));
1564 }
1565 match convert_query_body(query)? {
1566 QueryBody::Select(s) => Ok(*s),
1567 QueryBody::Compound(_) => Err(SqlError::Unsupported(
1568 "UNION/INTERSECT/EXCEPT in subqueries".into(),
1569 )),
1570 }
1571}
1572
1573fn convert_with(with: &sp::With) -> Result<(Vec<CteDefinition>, bool)> {
1574 let mut names = std::collections::HashSet::new();
1575 let mut ctes = Vec::new();
1576 for cte in &with.cte_tables {
1577 let name = cte.alias.name.value.to_ascii_lowercase();
1578 if !names.insert(name.clone()) {
1579 return Err(SqlError::DuplicateCteName(name));
1580 }
1581 let column_aliases: Vec<String> = cte
1582 .alias
1583 .columns
1584 .iter()
1585 .map(|c| c.name.value.to_ascii_lowercase())
1586 .collect();
1587 let body = convert_query_body(&cte.query)?;
1588 ctes.push(CteDefinition {
1589 name,
1590 column_aliases,
1591 body,
1592 });
1593 }
1594 Ok((ctes, with.recursive))
1595}
1596
1597fn convert_query(query: sp::Query) -> Result<Statement> {
1598 let (ctes, recursive) = if let Some(ref with) = query.with {
1599 convert_with(with)?
1600 } else {
1601 (vec![], false)
1602 };
1603 let body = convert_query_body(&query)?;
1604 Ok(Statement::Select(Box::new(SelectQuery {
1605 ctes,
1606 recursive,
1607 body,
1608 })))
1609}
1610
1611fn convert_join(join: &sp::Join) -> Result<JoinClause> {
1612 let (join_type, constraint) = match &join.join_operator {
1613 sp::JoinOperator::Inner(c) => (JoinType::Inner, Some(c)),
1614 sp::JoinOperator::Join(c) => (JoinType::Inner, Some(c)),
1615 sp::JoinOperator::CrossJoin(c) => (JoinType::Cross, Some(c)),
1616 sp::JoinOperator::Left(c) => (JoinType::Left, Some(c)),
1617 sp::JoinOperator::LeftSemi(c) => (JoinType::Left, Some(c)),
1618 sp::JoinOperator::LeftAnti(c) => (JoinType::Left, Some(c)),
1619 sp::JoinOperator::Right(c) => (JoinType::Right, Some(c)),
1620 sp::JoinOperator::RightSemi(c) => (JoinType::Right, Some(c)),
1621 sp::JoinOperator::RightAnti(c) => (JoinType::Right, Some(c)),
1622 other => return Err(SqlError::Unsupported(format!("join type: {other:?}"))),
1623 };
1624
1625 let (name, alias) = match &join.relation {
1626 sp::TableFactor::Table { name, alias, .. } => {
1627 let table_name = object_name_to_string(name);
1628 let alias_str = alias.as_ref().map(|a| a.name.value.clone());
1629 (table_name, alias_str)
1630 }
1631 _ => return Err(SqlError::Unsupported("non-table JOIN source".into())),
1632 };
1633
1634 let on_clause = match constraint {
1635 Some(sp::JoinConstraint::On(expr)) => Some(convert_expr(expr)?),
1636 Some(sp::JoinConstraint::None) | None => None,
1637 Some(other) => return Err(SqlError::Unsupported(format!("join constraint: {other:?}"))),
1638 };
1639
1640 Ok(JoinClause {
1641 join_type,
1642 table: TableRef { name, alias },
1643 on_clause,
1644 })
1645}
1646
1647fn convert_update(update: sp::Update) -> Result<Statement> {
1648 let table = match &update.table.relation {
1649 sp::TableFactor::Table { name, .. } => object_name_to_string(name),
1650 _ => return Err(SqlError::Unsupported("non-table UPDATE target".into())),
1651 };
1652
1653 let assignments = update
1654 .assignments
1655 .iter()
1656 .map(|a| {
1657 let col = match &a.target {
1658 sp::AssignmentTarget::ColumnName(name) => object_name_to_string(name),
1659 _ => return Err(SqlError::Unsupported("tuple assignment".into())),
1660 };
1661 let expr = convert_expr(&a.value)?;
1662 Ok((col, expr))
1663 })
1664 .collect::<Result<_>>()?;
1665
1666 let where_clause = update.selection.as_ref().map(convert_expr).transpose()?;
1667
1668 Ok(Statement::Update(UpdateStmt {
1669 table,
1670 assignments,
1671 where_clause,
1672 }))
1673}
1674
1675fn convert_delete(delete: sp::Delete) -> Result<Statement> {
1676 let table_name = match &delete.from {
1677 sp::FromTable::WithFromKeyword(tables) => {
1678 if tables.len() != 1 {
1679 return Err(SqlError::Unsupported("multi-table DELETE".into()));
1680 }
1681 match &tables[0].relation {
1682 sp::TableFactor::Table { name, .. } => object_name_to_string(name),
1683 _ => return Err(SqlError::Unsupported("non-table DELETE target".into())),
1684 }
1685 }
1686 sp::FromTable::WithoutKeyword(tables) => {
1687 if tables.len() != 1 {
1688 return Err(SqlError::Unsupported("multi-table DELETE".into()));
1689 }
1690 match &tables[0].relation {
1691 sp::TableFactor::Table { name, .. } => object_name_to_string(name),
1692 _ => return Err(SqlError::Unsupported("non-table DELETE target".into())),
1693 }
1694 }
1695 };
1696
1697 let where_clause = delete.selection.as_ref().map(convert_expr).transpose()?;
1698
1699 Ok(Statement::Delete(DeleteStmt {
1700 table: table_name,
1701 where_clause,
1702 }))
1703}
1704
1705fn convert_expr(expr: &sp::Expr) -> Result<Expr> {
1708 match expr {
1709 sp::Expr::Value(v) => convert_value(&v.value),
1710 sp::Expr::Identifier(ident) => Ok(Expr::Column(ident.value.to_ascii_lowercase())),
1711 sp::Expr::CompoundIdentifier(parts) => {
1712 if parts.len() == 2 {
1713 Ok(Expr::QualifiedColumn {
1714 table: parts[0].value.to_ascii_lowercase(),
1715 column: parts[1].value.to_ascii_lowercase(),
1716 })
1717 } else {
1718 Ok(Expr::Column(
1719 parts.last().unwrap().value.to_ascii_lowercase(),
1720 ))
1721 }
1722 }
1723 sp::Expr::BinaryOp { left, op, right } => {
1724 let bin_op = convert_bin_op(op)?;
1725 Ok(Expr::BinaryOp {
1726 left: Box::new(convert_expr(left)?),
1727 op: bin_op,
1728 right: Box::new(convert_expr(right)?),
1729 })
1730 }
1731 sp::Expr::UnaryOp { op, expr } => {
1732 let unary_op = match op {
1733 sp::UnaryOperator::Minus => UnaryOp::Neg,
1734 sp::UnaryOperator::Not => UnaryOp::Not,
1735 _ => return Err(SqlError::Unsupported(format!("unary op: {op}"))),
1736 };
1737 Ok(Expr::UnaryOp {
1738 op: unary_op,
1739 expr: Box::new(convert_expr(expr)?),
1740 })
1741 }
1742 sp::Expr::IsNull(e) => Ok(Expr::IsNull(Box::new(convert_expr(e)?))),
1743 sp::Expr::IsNotNull(e) => Ok(Expr::IsNotNull(Box::new(convert_expr(e)?))),
1744 sp::Expr::Nested(e) => convert_expr(e),
1745 sp::Expr::Function(func) => convert_function(func),
1746 sp::Expr::InSubquery {
1747 expr: e,
1748 subquery,
1749 negated,
1750 } => {
1751 let inner_expr = convert_expr(e)?;
1752 let stmt = convert_subquery(subquery)?;
1753 Ok(Expr::InSubquery {
1754 expr: Box::new(inner_expr),
1755 subquery: Box::new(stmt),
1756 negated: *negated,
1757 })
1758 }
1759 sp::Expr::InList {
1760 expr: e,
1761 list,
1762 negated,
1763 } => {
1764 let inner_expr = convert_expr(e)?;
1765 let items = list.iter().map(convert_expr).collect::<Result<Vec<_>>>()?;
1766 Ok(Expr::InList {
1767 expr: Box::new(inner_expr),
1768 list: items,
1769 negated: *negated,
1770 })
1771 }
1772 sp::Expr::Exists { subquery, negated } => {
1773 let stmt = convert_subquery(subquery)?;
1774 Ok(Expr::Exists {
1775 subquery: Box::new(stmt),
1776 negated: *negated,
1777 })
1778 }
1779 sp::Expr::Subquery(query) => {
1780 let stmt = convert_subquery(query)?;
1781 Ok(Expr::ScalarSubquery(Box::new(stmt)))
1782 }
1783 sp::Expr::Between {
1784 expr: e,
1785 negated,
1786 low,
1787 high,
1788 } => Ok(Expr::Between {
1789 expr: Box::new(convert_expr(e)?),
1790 low: Box::new(convert_expr(low)?),
1791 high: Box::new(convert_expr(high)?),
1792 negated: *negated,
1793 }),
1794 sp::Expr::Like {
1795 expr: e,
1796 negated,
1797 pattern,
1798 escape_char,
1799 ..
1800 } => {
1801 let esc = escape_char
1802 .as_ref()
1803 .map(convert_escape_value)
1804 .transpose()?
1805 .map(Box::new);
1806 Ok(Expr::Like {
1807 expr: Box::new(convert_expr(e)?),
1808 pattern: Box::new(convert_expr(pattern)?),
1809 escape: esc,
1810 negated: *negated,
1811 })
1812 }
1813 sp::Expr::ILike {
1814 expr: e,
1815 negated,
1816 pattern,
1817 escape_char,
1818 ..
1819 } => {
1820 let esc = escape_char
1821 .as_ref()
1822 .map(convert_escape_value)
1823 .transpose()?
1824 .map(Box::new);
1825 Ok(Expr::Like {
1826 expr: Box::new(convert_expr(e)?),
1827 pattern: Box::new(convert_expr(pattern)?),
1828 escape: esc,
1829 negated: *negated,
1830 })
1831 }
1832 sp::Expr::Case {
1833 operand,
1834 conditions,
1835 else_result,
1836 ..
1837 } => {
1838 let op = operand
1839 .as_ref()
1840 .map(|e| convert_expr(e))
1841 .transpose()?
1842 .map(Box::new);
1843 let conds: Vec<(Expr, Expr)> = conditions
1844 .iter()
1845 .map(|cw| Ok((convert_expr(&cw.condition)?, convert_expr(&cw.result)?)))
1846 .collect::<Result<_>>()?;
1847 let else_r = else_result
1848 .as_ref()
1849 .map(|e| convert_expr(e))
1850 .transpose()?
1851 .map(Box::new);
1852 Ok(Expr::Case {
1853 operand: op,
1854 conditions: conds,
1855 else_result: else_r,
1856 })
1857 }
1858 sp::Expr::Cast {
1859 expr: e,
1860 data_type: dt,
1861 ..
1862 } => {
1863 let target = convert_data_type(dt)?;
1864 Ok(Expr::Cast {
1865 expr: Box::new(convert_expr(e)?),
1866 data_type: target,
1867 })
1868 }
1869 sp::Expr::Substring {
1870 expr: e,
1871 substring_from,
1872 substring_for,
1873 ..
1874 } => {
1875 let mut args = vec![convert_expr(e)?];
1876 if let Some(from) = substring_from {
1877 args.push(convert_expr(from)?);
1878 }
1879 if let Some(f) = substring_for {
1880 args.push(convert_expr(f)?);
1881 }
1882 Ok(Expr::Function {
1883 name: "SUBSTR".into(),
1884 args,
1885 })
1886 }
1887 sp::Expr::Trim {
1888 expr: e,
1889 trim_where,
1890 trim_what,
1891 trim_characters,
1892 } => {
1893 let fn_name = match trim_where {
1894 Some(sp::TrimWhereField::Leading) => "LTRIM",
1895 Some(sp::TrimWhereField::Trailing) => "RTRIM",
1896 _ => "TRIM",
1897 };
1898 let mut args = vec![convert_expr(e)?];
1899 if let Some(what) = trim_what {
1900 args.push(convert_expr(what)?);
1901 } else if let Some(chars) = trim_characters {
1902 if let Some(first) = chars.first() {
1903 args.push(convert_expr(first)?);
1904 }
1905 }
1906 Ok(Expr::Function {
1907 name: fn_name.into(),
1908 args,
1909 })
1910 }
1911 sp::Expr::Ceil { expr: e, .. } => Ok(Expr::Function {
1912 name: "CEIL".into(),
1913 args: vec![convert_expr(e)?],
1914 }),
1915 sp::Expr::Floor { expr: e, .. } => Ok(Expr::Function {
1916 name: "FLOOR".into(),
1917 args: vec![convert_expr(e)?],
1918 }),
1919 sp::Expr::Position { expr: e, r#in } => Ok(Expr::Function {
1920 name: "INSTR".into(),
1921 args: vec![convert_expr(r#in)?, convert_expr(e)?],
1922 }),
1923 _ => Err(SqlError::Unsupported(format!("expression: {expr}"))),
1924 }
1925}
1926
1927fn convert_value(val: &sp::Value) -> Result<Expr> {
1928 match val {
1929 sp::Value::Number(n, _) => {
1930 if let Ok(i) = n.parse::<i64>() {
1931 Ok(Expr::Literal(Value::Integer(i)))
1932 } else if let Ok(f) = n.parse::<f64>() {
1933 Ok(Expr::Literal(Value::Real(f)))
1934 } else {
1935 Err(SqlError::InvalidValue(format!("cannot parse number: {n}")))
1936 }
1937 }
1938 sp::Value::SingleQuotedString(s) => Ok(Expr::Literal(Value::Text(s.as_str().into()))),
1939 sp::Value::Boolean(b) => Ok(Expr::Literal(Value::Boolean(*b))),
1940 sp::Value::Null => Ok(Expr::Literal(Value::Null)),
1941 sp::Value::Placeholder(s) => {
1942 let idx_str = s
1943 .strip_prefix('$')
1944 .ok_or_else(|| SqlError::Parse(format!("invalid placeholder: {s}")))?;
1945 let idx: usize = idx_str
1946 .parse()
1947 .map_err(|_| SqlError::Parse(format!("invalid placeholder index: {s}")))?;
1948 if idx == 0 {
1949 return Err(SqlError::Parse("placeholder index must be >= 1".into()));
1950 }
1951 Ok(Expr::Parameter(idx))
1952 }
1953 _ => Err(SqlError::Unsupported(format!("value type: {val}"))),
1954 }
1955}
1956
1957fn convert_escape_value(val: &sp::Value) -> Result<Expr> {
1958 match val {
1959 sp::Value::SingleQuotedString(s) => Ok(Expr::Literal(Value::Text(s.as_str().into()))),
1960 _ => Err(SqlError::Unsupported(format!("ESCAPE value: {val}"))),
1961 }
1962}
1963
1964fn convert_bin_op(op: &sp::BinaryOperator) -> Result<BinOp> {
1965 match op {
1966 sp::BinaryOperator::Plus => Ok(BinOp::Add),
1967 sp::BinaryOperator::Minus => Ok(BinOp::Sub),
1968 sp::BinaryOperator::Multiply => Ok(BinOp::Mul),
1969 sp::BinaryOperator::Divide => Ok(BinOp::Div),
1970 sp::BinaryOperator::Modulo => Ok(BinOp::Mod),
1971 sp::BinaryOperator::Eq => Ok(BinOp::Eq),
1972 sp::BinaryOperator::NotEq => Ok(BinOp::NotEq),
1973 sp::BinaryOperator::Lt => Ok(BinOp::Lt),
1974 sp::BinaryOperator::Gt => Ok(BinOp::Gt),
1975 sp::BinaryOperator::LtEq => Ok(BinOp::LtEq),
1976 sp::BinaryOperator::GtEq => Ok(BinOp::GtEq),
1977 sp::BinaryOperator::And => Ok(BinOp::And),
1978 sp::BinaryOperator::Or => Ok(BinOp::Or),
1979 sp::BinaryOperator::StringConcat => Ok(BinOp::Concat),
1980 _ => Err(SqlError::Unsupported(format!("binary op: {op}"))),
1981 }
1982}
1983
1984fn convert_function(func: &sp::Function) -> Result<Expr> {
1985 let name = object_name_to_string(&func.name).to_ascii_uppercase();
1986
1987 let (args, is_count_star) = match &func.args {
1988 sp::FunctionArguments::List(list) => {
1989 if list.args.is_empty() && name == "COUNT" {
1990 (vec![], true)
1991 } else {
1992 let mut count_star = false;
1993 let args = list
1994 .args
1995 .iter()
1996 .map(|arg| match arg {
1997 sp::FunctionArg::Unnamed(sp::FunctionArgExpr::Expr(e)) => convert_expr(e),
1998 sp::FunctionArg::Unnamed(sp::FunctionArgExpr::Wildcard) => {
1999 if name == "COUNT" {
2000 count_star = true;
2001 Ok(Expr::CountStar)
2002 } else {
2003 Err(SqlError::Unsupported(format!("{name}(*)")))
2004 }
2005 }
2006 _ => Err(SqlError::Unsupported(format!(
2007 "function arg type in {name}"
2008 ))),
2009 })
2010 .collect::<Result<Vec<_>>>()?;
2011 if name == "COUNT" && args.len() == 1 && count_star {
2012 (vec![], true)
2013 } else {
2014 (args, false)
2015 }
2016 }
2017 }
2018 sp::FunctionArguments::None => {
2019 if name == "COUNT" {
2020 (vec![], true)
2021 } else {
2022 (vec![], false)
2023 }
2024 }
2025 sp::FunctionArguments::Subquery(_) => {
2026 return Err(SqlError::Unsupported("subquery in function".into()));
2027 }
2028 };
2029
2030 if let Some(over) = &func.over {
2032 let spec = match over {
2033 sp::WindowType::WindowSpec(ws) => convert_window_spec(ws)?,
2034 sp::WindowType::NamedWindow(_) => {
2035 return Err(SqlError::Unsupported("named windows".into()));
2036 }
2037 };
2038 return Ok(Expr::WindowFunction { name, args, spec });
2039 }
2040
2041 if is_count_star {
2043 return Ok(Expr::CountStar);
2044 }
2045
2046 if name == "COALESCE" {
2047 if args.is_empty() {
2048 return Err(SqlError::Parse(
2049 "COALESCE requires at least one argument".into(),
2050 ));
2051 }
2052 return Ok(Expr::Coalesce(args));
2053 }
2054
2055 if name == "NULLIF" {
2056 if args.len() != 2 {
2057 return Err(SqlError::Parse(
2058 "NULLIF requires exactly two arguments".into(),
2059 ));
2060 }
2061 return Ok(Expr::Case {
2062 operand: None,
2063 conditions: vec![(
2064 Expr::BinaryOp {
2065 left: Box::new(args[0].clone()),
2066 op: BinOp::Eq,
2067 right: Box::new(args[1].clone()),
2068 },
2069 Expr::Literal(Value::Null),
2070 )],
2071 else_result: Some(Box::new(args[0].clone())),
2072 });
2073 }
2074
2075 if name == "IIF" {
2076 if args.len() != 3 {
2077 return Err(SqlError::Parse(
2078 "IIF requires exactly three arguments".into(),
2079 ));
2080 }
2081 return Ok(Expr::Case {
2082 operand: None,
2083 conditions: vec![(args[0].clone(), args[1].clone())],
2084 else_result: Some(Box::new(args[2].clone())),
2085 });
2086 }
2087
2088 Ok(Expr::Function { name, args })
2089}
2090
2091fn convert_window_spec(ws: &sp::WindowSpec) -> Result<WindowSpec> {
2092 let partition_by = ws
2093 .partition_by
2094 .iter()
2095 .map(convert_expr)
2096 .collect::<Result<Vec<_>>>()?;
2097 let order_by = ws
2098 .order_by
2099 .iter()
2100 .map(convert_order_by_expr)
2101 .collect::<Result<Vec<_>>>()?;
2102 let frame = ws
2103 .window_frame
2104 .as_ref()
2105 .map(convert_window_frame)
2106 .transpose()?;
2107 Ok(WindowSpec {
2108 partition_by,
2109 order_by,
2110 frame,
2111 })
2112}
2113
2114fn convert_window_frame(wf: &sp::WindowFrame) -> Result<WindowFrame> {
2115 let units = match wf.units {
2116 sp::WindowFrameUnits::Rows => WindowFrameUnits::Rows,
2117 sp::WindowFrameUnits::Range => WindowFrameUnits::Range,
2118 sp::WindowFrameUnits::Groups => {
2119 return Err(SqlError::Unsupported("GROUPS window frame".into()));
2120 }
2121 };
2122 let start = convert_window_frame_bound(&wf.start_bound)?;
2123 let end = match &wf.end_bound {
2124 Some(b) => convert_window_frame_bound(b)?,
2125 None => WindowFrameBound::CurrentRow,
2126 };
2127 Ok(WindowFrame { units, start, end })
2128}
2129
2130fn convert_window_frame_bound(b: &sp::WindowFrameBound) -> Result<WindowFrameBound> {
2131 match b {
2132 sp::WindowFrameBound::CurrentRow => Ok(WindowFrameBound::CurrentRow),
2133 sp::WindowFrameBound::Preceding(None) => Ok(WindowFrameBound::UnboundedPreceding),
2134 sp::WindowFrameBound::Preceding(Some(e)) => {
2135 Ok(WindowFrameBound::Preceding(Box::new(convert_expr(e)?)))
2136 }
2137 sp::WindowFrameBound::Following(None) => Ok(WindowFrameBound::UnboundedFollowing),
2138 sp::WindowFrameBound::Following(Some(e)) => {
2139 Ok(WindowFrameBound::Following(Box::new(convert_expr(e)?)))
2140 }
2141 }
2142}
2143
2144fn convert_select_item(item: &sp::SelectItem) -> Result<SelectColumn> {
2145 match item {
2146 sp::SelectItem::Wildcard(_) => Ok(SelectColumn::AllColumns),
2147 sp::SelectItem::UnnamedExpr(e) => {
2148 let expr = convert_expr(e)?;
2149 Ok(SelectColumn::Expr { expr, alias: None })
2150 }
2151 sp::SelectItem::ExprWithAlias { expr, alias } => {
2152 let expr = convert_expr(expr)?;
2153 Ok(SelectColumn::Expr {
2154 expr,
2155 alias: Some(alias.value.clone()),
2156 })
2157 }
2158 sp::SelectItem::QualifiedWildcard(_, _) => {
2159 Err(SqlError::Unsupported("qualified wildcard (table.*)".into()))
2160 }
2161 }
2162}
2163
2164fn convert_order_by_expr(expr: &sp::OrderByExpr) -> Result<OrderByItem> {
2165 let e = convert_expr(&expr.expr)?;
2166 let descending = expr.options.asc.map(|asc| !asc).unwrap_or(false);
2167 let nulls_first = expr.options.nulls_first;
2168
2169 Ok(OrderByItem {
2170 expr: e,
2171 descending,
2172 nulls_first,
2173 })
2174}
2175
2176fn convert_data_type(dt: &sp::DataType) -> Result<DataType> {
2179 match dt {
2180 sp::DataType::Int(_)
2181 | sp::DataType::Integer(_)
2182 | sp::DataType::BigInt(_)
2183 | sp::DataType::SmallInt(_)
2184 | sp::DataType::TinyInt(_)
2185 | sp::DataType::Int2(_)
2186 | sp::DataType::Int4(_)
2187 | sp::DataType::Int8(_) => Ok(DataType::Integer),
2188
2189 sp::DataType::Real
2190 | sp::DataType::Double(..)
2191 | sp::DataType::DoublePrecision
2192 | sp::DataType::Float(_)
2193 | sp::DataType::Float4
2194 | sp::DataType::Float64 => Ok(DataType::Real),
2195
2196 sp::DataType::Varchar(_)
2197 | sp::DataType::Text
2198 | sp::DataType::Char(_)
2199 | sp::DataType::Character(_)
2200 | sp::DataType::String(_) => Ok(DataType::Text),
2201
2202 sp::DataType::Blob(_) | sp::DataType::Bytea => Ok(DataType::Blob),
2203
2204 sp::DataType::Boolean | sp::DataType::Bool => Ok(DataType::Boolean),
2205
2206 _ => Err(SqlError::Unsupported(format!("data type: {dt}"))),
2207 }
2208}
2209
2210fn object_name_to_string(name: &sp::ObjectName) -> String {
2213 name.0
2214 .iter()
2215 .filter_map(|p| match p {
2216 sp::ObjectNamePart::Identifier(ident) => Some(ident.value.clone()),
2217 _ => None,
2218 })
2219 .collect::<Vec<_>>()
2220 .join(".")
2221}
2222
2223#[cfg(test)]
2224mod tests {
2225 use super::*;
2226
2227 #[test]
2228 fn parse_create_table() {
2229 let stmt = parse_sql(
2230 "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER)",
2231 )
2232 .unwrap();
2233
2234 match stmt {
2235 Statement::CreateTable(ct) => {
2236 assert_eq!(ct.name, "users");
2237 assert_eq!(ct.columns.len(), 3);
2238 assert_eq!(ct.columns[0].name, "id");
2239 assert_eq!(ct.columns[0].data_type, DataType::Integer);
2240 assert!(ct.columns[0].is_primary_key);
2241 assert!(!ct.columns[0].nullable);
2242 assert_eq!(ct.columns[1].name, "name");
2243 assert_eq!(ct.columns[1].data_type, DataType::Text);
2244 assert!(!ct.columns[1].nullable);
2245 assert_eq!(ct.columns[2].name, "age");
2246 assert!(ct.columns[2].nullable);
2247 assert_eq!(ct.primary_key, vec!["id"]);
2248 }
2249 _ => panic!("expected CreateTable"),
2250 }
2251 }
2252
2253 #[test]
2254 fn parse_create_table_if_not_exists() {
2255 let stmt = parse_sql("CREATE TABLE IF NOT EXISTS t (id INT PRIMARY KEY)").unwrap();
2256 match stmt {
2257 Statement::CreateTable(ct) => assert!(ct.if_not_exists),
2258 _ => panic!("expected CreateTable"),
2259 }
2260 }
2261
2262 #[test]
2263 fn parse_drop_table() {
2264 let stmt = parse_sql("DROP TABLE users").unwrap();
2265 match stmt {
2266 Statement::DropTable(dt) => {
2267 assert_eq!(dt.name, "users");
2268 assert!(!dt.if_exists);
2269 }
2270 _ => panic!("expected DropTable"),
2271 }
2272 }
2273
2274 #[test]
2275 fn parse_drop_table_if_exists() {
2276 let stmt = parse_sql("DROP TABLE IF EXISTS users").unwrap();
2277 match stmt {
2278 Statement::DropTable(dt) => assert!(dt.if_exists),
2279 _ => panic!("expected DropTable"),
2280 }
2281 }
2282
2283 #[test]
2284 fn parse_insert() {
2285 let stmt =
2286 parse_sql("INSERT INTO users (id, name) VALUES (1, 'Alice'), (2, 'Bob')").unwrap();
2287
2288 match stmt {
2289 Statement::Insert(ins) => {
2290 assert_eq!(ins.table, "users");
2291 assert_eq!(ins.columns, vec!["id", "name"]);
2292 let values = match &ins.source {
2293 InsertSource::Values(v) => v,
2294 _ => panic!("expected Values"),
2295 };
2296 assert_eq!(values.len(), 2);
2297 assert!(matches!(values[0][0], Expr::Literal(Value::Integer(1))));
2298 assert!(matches!(&values[0][1], Expr::Literal(Value::Text(s)) if s == "Alice"));
2299 }
2300 _ => panic!("expected Insert"),
2301 }
2302 }
2303
2304 #[test]
2305 fn parse_select_all() {
2306 let stmt = parse_sql("SELECT * FROM users").unwrap();
2307 match stmt {
2308 Statement::Select(sq) => match sq.body {
2309 QueryBody::Select(sel) => {
2310 assert_eq!(sel.from, "users");
2311 assert!(matches!(sel.columns[0], SelectColumn::AllColumns));
2312 assert!(sel.where_clause.is_none());
2313 }
2314 _ => panic!("expected QueryBody::Select"),
2315 },
2316 _ => panic!("expected Select"),
2317 }
2318 }
2319
2320 #[test]
2321 fn parse_select_where() {
2322 let stmt = parse_sql("SELECT id, name FROM users WHERE age > 18").unwrap();
2323 match stmt {
2324 Statement::Select(sq) => match sq.body {
2325 QueryBody::Select(sel) => {
2326 assert_eq!(sel.columns.len(), 2);
2327 assert!(sel.where_clause.is_some());
2328 }
2329 _ => panic!("expected QueryBody::Select"),
2330 },
2331 _ => panic!("expected Select"),
2332 }
2333 }
2334
2335 #[test]
2336 fn parse_select_order_limit() {
2337 let stmt = parse_sql("SELECT * FROM users ORDER BY name ASC LIMIT 10 OFFSET 5").unwrap();
2338 match stmt {
2339 Statement::Select(sq) => match sq.body {
2340 QueryBody::Select(sel) => {
2341 assert_eq!(sel.order_by.len(), 1);
2342 assert!(!sel.order_by[0].descending);
2343 assert!(sel.limit.is_some());
2344 assert!(sel.offset.is_some());
2345 }
2346 _ => panic!("expected QueryBody::Select"),
2347 },
2348 _ => panic!("expected Select"),
2349 }
2350 }
2351
2352 #[test]
2353 fn parse_update() {
2354 let stmt = parse_sql("UPDATE users SET name = 'Bob' WHERE id = 1").unwrap();
2355 match stmt {
2356 Statement::Update(upd) => {
2357 assert_eq!(upd.table, "users");
2358 assert_eq!(upd.assignments.len(), 1);
2359 assert_eq!(upd.assignments[0].0, "name");
2360 assert!(upd.where_clause.is_some());
2361 }
2362 _ => panic!("expected Update"),
2363 }
2364 }
2365
2366 #[test]
2367 fn parse_delete() {
2368 let stmt = parse_sql("DELETE FROM users WHERE id = 1").unwrap();
2369 match stmt {
2370 Statement::Delete(del) => {
2371 assert_eq!(del.table, "users");
2372 assert!(del.where_clause.is_some());
2373 }
2374 _ => panic!("expected Delete"),
2375 }
2376 }
2377
2378 #[test]
2379 fn parse_aggregate() {
2380 let stmt = parse_sql("SELECT COUNT(*), SUM(age) FROM users").unwrap();
2381 match stmt {
2382 Statement::Select(sq) => match sq.body {
2383 QueryBody::Select(sel) => {
2384 assert_eq!(sel.columns.len(), 2);
2385 match &sel.columns[0] {
2386 SelectColumn::Expr {
2387 expr: Expr::CountStar,
2388 ..
2389 } => {}
2390 other => panic!("expected CountStar, got {other:?}"),
2391 }
2392 }
2393 _ => panic!("expected QueryBody::Select"),
2394 },
2395 _ => panic!("expected Select"),
2396 }
2397 }
2398
2399 #[test]
2400 fn parse_group_by_having() {
2401 let stmt = parse_sql(
2402 "SELECT department, COUNT(*) FROM employees GROUP BY department HAVING COUNT(*) > 5",
2403 )
2404 .unwrap();
2405 match stmt {
2406 Statement::Select(sq) => match sq.body {
2407 QueryBody::Select(sel) => {
2408 assert_eq!(sel.group_by.len(), 1);
2409 assert!(sel.having.is_some());
2410 }
2411 _ => panic!("expected QueryBody::Select"),
2412 },
2413 _ => panic!("expected Select"),
2414 }
2415 }
2416
2417 #[test]
2418 fn parse_expressions() {
2419 let stmt = parse_sql("SELECT id + 1, -price, NOT active FROM items").unwrap();
2420 match stmt {
2421 Statement::Select(sq) => match sq.body {
2422 QueryBody::Select(sel) => {
2423 assert_eq!(sel.columns.len(), 3);
2424 match &sel.columns[0] {
2426 SelectColumn::Expr {
2427 expr: Expr::BinaryOp { op: BinOp::Add, .. },
2428 ..
2429 } => {}
2430 other => panic!("expected BinaryOp Add, got {other:?}"),
2431 }
2432 match &sel.columns[1] {
2434 SelectColumn::Expr {
2435 expr:
2436 Expr::UnaryOp {
2437 op: UnaryOp::Neg, ..
2438 },
2439 ..
2440 } => {}
2441 other => panic!("expected UnaryOp Neg, got {other:?}"),
2442 }
2443 match &sel.columns[2] {
2445 SelectColumn::Expr {
2446 expr:
2447 Expr::UnaryOp {
2448 op: UnaryOp::Not, ..
2449 },
2450 ..
2451 } => {}
2452 other => panic!("expected UnaryOp Not, got {other:?}"),
2453 }
2454 }
2455 _ => panic!("expected QueryBody::Select"),
2456 },
2457 _ => panic!("expected Select"),
2458 }
2459 }
2460
2461 #[test]
2462 fn parse_is_null() {
2463 let stmt = parse_sql("SELECT * FROM t WHERE x IS NULL").unwrap();
2464 match stmt {
2465 Statement::Select(sq) => match sq.body {
2466 QueryBody::Select(sel) => {
2467 assert!(matches!(sel.where_clause, Some(Expr::IsNull(_))));
2468 }
2469 _ => panic!("expected QueryBody::Select"),
2470 },
2471 _ => panic!("expected Select"),
2472 }
2473 }
2474
2475 #[test]
2476 fn parse_inner_join() {
2477 let stmt = parse_sql("SELECT * FROM a JOIN b ON a.id = b.id").unwrap();
2478 match stmt {
2479 Statement::Select(sq) => match sq.body {
2480 QueryBody::Select(sel) => {
2481 assert_eq!(sel.from, "a");
2482 assert_eq!(sel.joins.len(), 1);
2483 assert_eq!(sel.joins[0].join_type, JoinType::Inner);
2484 assert_eq!(sel.joins[0].table.name, "b");
2485 assert!(sel.joins[0].on_clause.is_some());
2486 }
2487 _ => panic!("expected QueryBody::Select"),
2488 },
2489 _ => panic!("expected Select"),
2490 }
2491 }
2492
2493 #[test]
2494 fn parse_inner_join_explicit() {
2495 let stmt = parse_sql("SELECT * FROM a INNER JOIN b ON a.id = b.a_id").unwrap();
2496 match stmt {
2497 Statement::Select(sq) => match sq.body {
2498 QueryBody::Select(sel) => {
2499 assert_eq!(sel.joins.len(), 1);
2500 assert_eq!(sel.joins[0].join_type, JoinType::Inner);
2501 }
2502 _ => panic!("expected QueryBody::Select"),
2503 },
2504 _ => panic!("expected Select"),
2505 }
2506 }
2507
2508 #[test]
2509 fn parse_cross_join() {
2510 let stmt = parse_sql("SELECT * FROM a CROSS JOIN b").unwrap();
2511 match stmt {
2512 Statement::Select(sq) => match sq.body {
2513 QueryBody::Select(sel) => {
2514 assert_eq!(sel.joins.len(), 1);
2515 assert_eq!(sel.joins[0].join_type, JoinType::Cross);
2516 assert!(sel.joins[0].on_clause.is_none());
2517 }
2518 _ => panic!("expected QueryBody::Select"),
2519 },
2520 _ => panic!("expected Select"),
2521 }
2522 }
2523
2524 #[test]
2525 fn parse_left_join() {
2526 let stmt = parse_sql("SELECT * FROM a LEFT JOIN b ON a.id = b.a_id").unwrap();
2527 match stmt {
2528 Statement::Select(sq) => match sq.body {
2529 QueryBody::Select(sel) => {
2530 assert_eq!(sel.joins.len(), 1);
2531 assert_eq!(sel.joins[0].join_type, JoinType::Left);
2532 }
2533 _ => panic!("expected QueryBody::Select"),
2534 },
2535 _ => panic!("expected Select"),
2536 }
2537 }
2538
2539 #[test]
2540 fn parse_table_alias() {
2541 let stmt = parse_sql("SELECT u.id FROM users u JOIN orders o ON u.id = o.user_id").unwrap();
2542 match stmt {
2543 Statement::Select(sq) => match sq.body {
2544 QueryBody::Select(sel) => {
2545 assert_eq!(sel.from, "users");
2546 assert_eq!(sel.from_alias.as_deref(), Some("u"));
2547 assert_eq!(sel.joins[0].table.name, "orders");
2548 assert_eq!(sel.joins[0].table.alias.as_deref(), Some("o"));
2549 }
2550 _ => panic!("expected QueryBody::Select"),
2551 },
2552 _ => panic!("expected Select"),
2553 }
2554 }
2555
2556 #[test]
2557 fn parse_multi_join() {
2558 let stmt =
2559 parse_sql("SELECT * FROM a JOIN b ON a.id = b.a_id JOIN c ON b.id = c.b_id").unwrap();
2560 match stmt {
2561 Statement::Select(sq) => match sq.body {
2562 QueryBody::Select(sel) => {
2563 assert_eq!(sel.joins.len(), 2);
2564 }
2565 _ => panic!("expected QueryBody::Select"),
2566 },
2567 _ => panic!("expected Select"),
2568 }
2569 }
2570
2571 #[test]
2572 fn parse_qualified_column() {
2573 let stmt = parse_sql("SELECT u.id, u.name FROM users u").unwrap();
2574 match stmt {
2575 Statement::Select(sq) => match sq.body {
2576 QueryBody::Select(sel) => match &sel.columns[0] {
2577 SelectColumn::Expr {
2578 expr: Expr::QualifiedColumn { table, column },
2579 ..
2580 } => {
2581 assert_eq!(table, "u");
2582 assert_eq!(column, "id");
2583 }
2584 other => panic!("expected QualifiedColumn, got {other:?}"),
2585 },
2586 _ => panic!("expected QueryBody::Select"),
2587 },
2588 _ => panic!("expected Select"),
2589 }
2590 }
2591
2592 #[test]
2593 fn reject_subquery() {
2594 assert!(parse_sql("SELECT * FROM (SELECT 1)").is_err());
2595 }
2596
2597 #[test]
2598 fn parse_type_mapping() {
2599 let stmt = parse_sql(
2600 "CREATE TABLE t (a INT PRIMARY KEY, b BIGINT, c SMALLINT, d REAL, e DOUBLE PRECISION, f VARCHAR(255), g BOOLEAN, h BLOB, i BYTEA)"
2601 ).unwrap();
2602 match stmt {
2603 Statement::CreateTable(ct) => {
2604 assert_eq!(ct.columns[0].data_type, DataType::Integer); assert_eq!(ct.columns[1].data_type, DataType::Integer); assert_eq!(ct.columns[2].data_type, DataType::Integer); assert_eq!(ct.columns[3].data_type, DataType::Real); assert_eq!(ct.columns[4].data_type, DataType::Real); assert_eq!(ct.columns[5].data_type, DataType::Text); assert_eq!(ct.columns[6].data_type, DataType::Boolean); assert_eq!(ct.columns[7].data_type, DataType::Blob); assert_eq!(ct.columns[8].data_type, DataType::Blob); }
2614 _ => panic!("expected CreateTable"),
2615 }
2616 }
2617
2618 #[test]
2619 fn parse_boolean_literals() {
2620 let stmt = parse_sql("INSERT INTO t (a, b) VALUES (true, false)").unwrap();
2621 match stmt {
2622 Statement::Insert(ins) => {
2623 let values = match &ins.source {
2624 InsertSource::Values(v) => v,
2625 _ => panic!("expected Values"),
2626 };
2627 assert!(matches!(values[0][0], Expr::Literal(Value::Boolean(true))));
2628 assert!(matches!(values[0][1], Expr::Literal(Value::Boolean(false))));
2629 }
2630 _ => panic!("expected Insert"),
2631 }
2632 }
2633
2634 #[test]
2635 fn parse_null_literal() {
2636 let stmt = parse_sql("INSERT INTO t (a) VALUES (NULL)").unwrap();
2637 match stmt {
2638 Statement::Insert(ins) => {
2639 let values = match &ins.source {
2640 InsertSource::Values(v) => v,
2641 _ => panic!("expected Values"),
2642 };
2643 assert!(matches!(values[0][0], Expr::Literal(Value::Null)));
2644 }
2645 _ => panic!("expected Insert"),
2646 }
2647 }
2648
2649 #[test]
2650 fn parse_alias() {
2651 let stmt = parse_sql("SELECT id AS user_id FROM users").unwrap();
2652 match stmt {
2653 Statement::Select(sq) => match sq.body {
2654 QueryBody::Select(sel) => match &sel.columns[0] {
2655 SelectColumn::Expr { alias: Some(a), .. } => assert_eq!(a, "user_id"),
2656 other => panic!("expected alias, got {other:?}"),
2657 },
2658 _ => panic!("expected QueryBody::Select"),
2659 },
2660 _ => panic!("expected Select"),
2661 }
2662 }
2663
2664 #[test]
2665 fn parse_begin() {
2666 let stmt = parse_sql("BEGIN").unwrap();
2667 assert!(matches!(stmt, Statement::Begin));
2668 }
2669
2670 #[test]
2671 fn parse_begin_transaction() {
2672 let stmt = parse_sql("BEGIN TRANSACTION").unwrap();
2673 assert!(matches!(stmt, Statement::Begin));
2674 }
2675
2676 #[test]
2677 fn parse_commit() {
2678 let stmt = parse_sql("COMMIT").unwrap();
2679 assert!(matches!(stmt, Statement::Commit));
2680 }
2681
2682 #[test]
2683 fn parse_rollback() {
2684 let stmt = parse_sql("ROLLBACK").unwrap();
2685 assert!(matches!(stmt, Statement::Rollback));
2686 }
2687
2688 #[test]
2689 fn parse_select_distinct() {
2690 let stmt = parse_sql("SELECT DISTINCT name FROM users").unwrap();
2691 match stmt {
2692 Statement::Select(sq) => match sq.body {
2693 QueryBody::Select(sel) => {
2694 assert!(sel.distinct);
2695 assert_eq!(sel.columns.len(), 1);
2696 }
2697 _ => panic!("expected QueryBody::Select"),
2698 },
2699 _ => panic!("expected Select"),
2700 }
2701 }
2702
2703 #[test]
2704 fn parse_select_without_distinct() {
2705 let stmt = parse_sql("SELECT name FROM users").unwrap();
2706 match stmt {
2707 Statement::Select(sq) => match sq.body {
2708 QueryBody::Select(sel) => {
2709 assert!(!sel.distinct);
2710 }
2711 _ => panic!("expected QueryBody::Select"),
2712 },
2713 _ => panic!("expected Select"),
2714 }
2715 }
2716
2717 #[test]
2718 fn parse_select_distinct_all_columns() {
2719 let stmt = parse_sql("SELECT DISTINCT * FROM users").unwrap();
2720 match stmt {
2721 Statement::Select(sq) => match sq.body {
2722 QueryBody::Select(sel) => {
2723 assert!(sel.distinct);
2724 assert!(matches!(sel.columns[0], SelectColumn::AllColumns));
2725 }
2726 _ => panic!("expected QueryBody::Select"),
2727 },
2728 _ => panic!("expected Select"),
2729 }
2730 }
2731
2732 #[test]
2733 fn reject_distinct_on() {
2734 assert!(parse_sql("SELECT DISTINCT ON (id) * FROM users").is_err());
2735 }
2736
2737 #[test]
2738 fn parse_create_index() {
2739 let stmt = parse_sql("CREATE INDEX idx_name ON users (name)").unwrap();
2740 match stmt {
2741 Statement::CreateIndex(ci) => {
2742 assert_eq!(ci.index_name, "idx_name");
2743 assert_eq!(ci.table_name, "users");
2744 assert_eq!(ci.columns, vec!["name"]);
2745 assert!(!ci.unique);
2746 assert!(!ci.if_not_exists);
2747 }
2748 _ => panic!("expected CreateIndex"),
2749 }
2750 }
2751
2752 #[test]
2753 fn parse_create_unique_index() {
2754 let stmt = parse_sql("CREATE UNIQUE INDEX idx_email ON users (email)").unwrap();
2755 match stmt {
2756 Statement::CreateIndex(ci) => {
2757 assert!(ci.unique);
2758 assert_eq!(ci.columns, vec!["email"]);
2759 }
2760 _ => panic!("expected CreateIndex"),
2761 }
2762 }
2763
2764 #[test]
2765 fn parse_create_index_if_not_exists() {
2766 let stmt = parse_sql("CREATE INDEX IF NOT EXISTS idx_x ON t (a)").unwrap();
2767 match stmt {
2768 Statement::CreateIndex(ci) => assert!(ci.if_not_exists),
2769 _ => panic!("expected CreateIndex"),
2770 }
2771 }
2772
2773 #[test]
2774 fn parse_create_index_multi_column() {
2775 let stmt = parse_sql("CREATE INDEX idx_multi ON t (a, b, c)").unwrap();
2776 match stmt {
2777 Statement::CreateIndex(ci) => {
2778 assert_eq!(ci.columns, vec!["a", "b", "c"]);
2779 }
2780 _ => panic!("expected CreateIndex"),
2781 }
2782 }
2783
2784 #[test]
2785 fn parse_drop_index() {
2786 let stmt = parse_sql("DROP INDEX idx_name").unwrap();
2787 match stmt {
2788 Statement::DropIndex(di) => {
2789 assert_eq!(di.index_name, "idx_name");
2790 assert!(!di.if_exists);
2791 }
2792 _ => panic!("expected DropIndex"),
2793 }
2794 }
2795
2796 #[test]
2797 fn parse_drop_index_if_exists() {
2798 let stmt = parse_sql("DROP INDEX IF EXISTS idx_name").unwrap();
2799 match stmt {
2800 Statement::DropIndex(di) => {
2801 assert!(di.if_exists);
2802 }
2803 _ => panic!("expected DropIndex"),
2804 }
2805 }
2806
2807 #[test]
2808 fn parse_explain_select() {
2809 let stmt = parse_sql("EXPLAIN SELECT * FROM users WHERE id = 1").unwrap();
2810 match stmt {
2811 Statement::Explain(inner) => {
2812 assert!(matches!(*inner, Statement::Select(_)));
2813 }
2814 _ => panic!("expected Explain"),
2815 }
2816 }
2817
2818 #[test]
2819 fn parse_explain_insert() {
2820 let stmt = parse_sql("EXPLAIN INSERT INTO t (a) VALUES (1)").unwrap();
2821 assert!(matches!(stmt, Statement::Explain(_)));
2822 }
2823
2824 #[test]
2825 fn reject_explain_analyze() {
2826 assert!(parse_sql("EXPLAIN ANALYZE SELECT * FROM t").is_err());
2827 }
2828
2829 #[test]
2830 fn parse_parameter_placeholder() {
2831 let stmt = parse_sql("SELECT * FROM t WHERE id = $1").unwrap();
2832 match stmt {
2833 Statement::Select(sq) => match sq.body {
2834 QueryBody::Select(sel) => match &sel.where_clause {
2835 Some(Expr::BinaryOp { right, .. }) => {
2836 assert!(matches!(right.as_ref(), Expr::Parameter(1)));
2837 }
2838 other => panic!("expected BinaryOp with Parameter, got {other:?}"),
2839 },
2840 _ => panic!("expected QueryBody::Select"),
2841 },
2842 _ => panic!("expected Select"),
2843 }
2844 }
2845
2846 #[test]
2847 fn parse_multiple_parameters() {
2848 let stmt = parse_sql("INSERT INTO t (a, b) VALUES ($1, $2)").unwrap();
2849 match stmt {
2850 Statement::Insert(ins) => {
2851 let values = match &ins.source {
2852 InsertSource::Values(v) => v,
2853 _ => panic!("expected Values"),
2854 };
2855 assert!(matches!(values[0][0], Expr::Parameter(1)));
2856 assert!(matches!(values[0][1], Expr::Parameter(2)));
2857 }
2858 _ => panic!("expected Insert"),
2859 }
2860 }
2861
2862 #[test]
2863 fn parse_insert_select() {
2864 let stmt =
2865 parse_sql("INSERT INTO t2 (id, name) SELECT id, name FROM t1 WHERE id > 5").unwrap();
2866 match stmt {
2867 Statement::Insert(ins) => {
2868 assert_eq!(ins.table, "t2");
2869 assert_eq!(ins.columns, vec!["id", "name"]);
2870 match &ins.source {
2871 InsertSource::Select(sq) => match &sq.body {
2872 QueryBody::Select(sel) => {
2873 assert_eq!(sel.from, "t1");
2874 assert_eq!(sel.columns.len(), 2);
2875 assert!(sel.where_clause.is_some());
2876 }
2877 _ => panic!("expected QueryBody::Select"),
2878 },
2879 _ => panic!("expected InsertSource::Select"),
2880 }
2881 }
2882 _ => panic!("expected Insert"),
2883 }
2884 }
2885
2886 #[test]
2887 fn parse_insert_select_no_columns() {
2888 let stmt = parse_sql("INSERT INTO t2 SELECT * FROM t1").unwrap();
2889 match stmt {
2890 Statement::Insert(ins) => {
2891 assert_eq!(ins.table, "t2");
2892 assert!(ins.columns.is_empty());
2893 assert!(matches!(&ins.source, InsertSource::Select(_)));
2894 }
2895 _ => panic!("expected Insert"),
2896 }
2897 }
2898
2899 #[test]
2900 fn reject_zero_parameter() {
2901 assert!(parse_sql("SELECT $0 FROM t").is_err());
2902 }
2903
2904 #[test]
2905 fn count_params_basic() {
2906 let stmt = parse_sql("SELECT * FROM t WHERE a = $1 AND b = $3").unwrap();
2907 assert_eq!(count_params(&stmt), 3);
2908 }
2909
2910 #[test]
2911 fn count_params_none() {
2912 let stmt = parse_sql("SELECT * FROM t WHERE a = 1").unwrap();
2913 assert_eq!(count_params(&stmt), 0);
2914 }
2915
2916 #[test]
2917 fn bind_params_basic() {
2918 let stmt = parse_sql("SELECT * FROM t WHERE id = $1").unwrap();
2919 let bound = bind_params(&stmt, &[Value::Integer(42)]).unwrap();
2920 match bound {
2921 Statement::Select(sq) => match sq.body {
2922 QueryBody::Select(sel) => match &sel.where_clause {
2923 Some(Expr::BinaryOp { right, .. }) => {
2924 assert!(matches!(right.as_ref(), Expr::Literal(Value::Integer(42))));
2925 }
2926 other => panic!("expected BinaryOp with Literal, got {other:?}"),
2927 },
2928 _ => panic!("expected QueryBody::Select"),
2929 },
2930 _ => panic!("expected Select"),
2931 }
2932 }
2933
2934 #[test]
2935 fn bind_params_out_of_range() {
2936 let stmt = parse_sql("SELECT * FROM t WHERE id = $2").unwrap();
2937 let result = bind_params(&stmt, &[Value::Integer(1)]);
2938 assert!(result.is_err());
2939 }
2940
2941 #[test]
2942 fn parse_table_constraint_pk() {
2943 let stmt = parse_sql("CREATE TABLE t (a INTEGER, b TEXT, PRIMARY KEY (a))").unwrap();
2944 match stmt {
2945 Statement::CreateTable(ct) => {
2946 assert_eq!(ct.primary_key, vec!["a"]);
2947 assert!(ct.columns[0].is_primary_key);
2948 assert!(!ct.columns[0].nullable);
2949 }
2950 _ => panic!("expected CreateTable"),
2951 }
2952 }
2953}