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)]
11pub enum Statement {
12 CreateTable(CreateTableStmt),
13 DropTable(DropTableStmt),
14 CreateIndex(CreateIndexStmt),
15 DropIndex(DropIndexStmt),
16 CreateView(CreateViewStmt),
17 DropView(DropViewStmt),
18 AlterTable(Box<AlterTableStmt>),
19 Insert(InsertStmt),
20 Select(Box<SelectQuery>),
21 Update(UpdateStmt),
22 Delete(DeleteStmt),
23 Begin,
24 Commit,
25 Rollback,
26 Savepoint(String),
27 ReleaseSavepoint(String),
28 RollbackTo(String),
29 SetTimezone(String),
30 Explain(Box<Statement>),
31}
32
33#[derive(Debug, Clone)]
34pub struct AlterTableStmt {
35 pub table: String,
36 pub op: AlterTableOp,
37}
38
39#[derive(Debug, Clone)]
40pub enum AlterTableOp {
41 AddColumn {
42 column: Box<ColumnSpec>,
43 foreign_key: Option<ForeignKeyDef>,
44 if_not_exists: bool,
45 },
46 DropColumn {
47 name: String,
48 if_exists: bool,
49 },
50 RenameColumn {
51 old_name: String,
52 new_name: String,
53 },
54 RenameTable {
55 new_name: String,
56 },
57}
58
59#[derive(Debug, Clone)]
60pub struct CreateTableStmt {
61 pub name: String,
62 pub columns: Vec<ColumnSpec>,
63 pub primary_key: Vec<String>,
64 pub if_not_exists: bool,
65 pub check_constraints: Vec<TableCheckConstraint>,
66 pub foreign_keys: Vec<ForeignKeyDef>,
67 pub unique_indices: Vec<UniqueIndexDef>,
68}
69
70#[derive(Debug, Clone)]
71pub struct UniqueIndexDef {
72 pub name: Option<String>,
73 pub columns: Vec<String>,
74}
75
76#[derive(Debug, Clone)]
77pub struct TableCheckConstraint {
78 pub name: Option<String>,
79 pub expr: Expr,
80 pub sql: String,
81}
82
83#[derive(Debug, Clone)]
84pub struct ForeignKeyDef {
85 pub name: Option<String>,
86 pub columns: Vec<String>,
87 pub foreign_table: String,
88 pub referred_columns: Vec<String>,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum GeneratedKind {
93 Stored,
94 Virtual,
95}
96
97#[derive(Debug, Clone)]
98pub struct ColumnSpec {
99 pub name: String,
100 pub data_type: DataType,
101 pub nullable: bool,
102 pub is_primary_key: bool,
103 pub default_expr: Option<Expr>,
104 pub default_sql: Option<String>,
105 pub check_expr: Option<Expr>,
106 pub check_sql: Option<String>,
107 pub check_name: Option<String>,
108 pub generated_expr: Option<Expr>,
109 pub generated_sql: Option<String>,
110 pub generated_kind: Option<GeneratedKind>,
111}
112
113#[derive(Debug, Clone)]
114pub struct DropTableStmt {
115 pub name: String,
116 pub if_exists: bool,
117}
118
119#[derive(Debug, Clone)]
120pub struct CreateIndexStmt {
121 pub index_name: String,
122 pub table_name: String,
123 pub columns: Vec<String>,
124 pub unique: bool,
125 pub if_not_exists: bool,
126}
127
128#[derive(Debug, Clone)]
129pub struct DropIndexStmt {
130 pub index_name: String,
131 pub if_exists: bool,
132}
133
134#[derive(Debug, Clone)]
135pub struct CreateViewStmt {
136 pub name: String,
137 pub sql: String,
138 pub column_aliases: Vec<String>,
139 pub or_replace: bool,
140 pub if_not_exists: bool,
141}
142
143#[derive(Debug, Clone)]
144pub struct DropViewStmt {
145 pub name: String,
146 pub if_exists: bool,
147}
148
149#[derive(Debug, Clone)]
150pub enum InsertSource {
151 Values(Vec<Vec<Expr>>),
152 Select(Box<SelectQuery>),
153}
154
155#[derive(Debug, Clone)]
156pub struct InsertStmt {
157 pub table: String,
158 pub columns: Vec<String>,
159 pub source: InsertSource,
160 pub on_conflict: Option<OnConflictClause>,
161 pub returning: Option<Vec<SelectColumn>>,
162}
163
164#[derive(Debug, Clone)]
165pub struct OnConflictClause {
166 pub target: Option<ConflictTarget>,
167 pub action: OnConflictAction,
168}
169
170#[derive(Debug, Clone)]
171pub enum ConflictTarget {
172 Columns(Vec<String>),
173 Constraint(String),
174}
175
176#[derive(Debug, Clone)]
177pub enum OnConflictAction {
178 DoNothing,
179 DoUpdate {
180 assignments: Vec<(String, Expr)>,
181 where_clause: Option<Expr>,
182 },
183}
184
185#[derive(Debug, Clone)]
186pub struct TableRef {
187 pub name: String,
188 pub alias: Option<String>,
189}
190
191#[derive(Debug, Clone, Copy, PartialEq)]
192pub enum JoinType {
193 Inner,
194 Cross,
195 Left,
196 Right,
197}
198
199#[derive(Debug, Clone)]
200pub struct JoinClause {
201 pub join_type: JoinType,
202 pub table: TableRef,
203 pub on_clause: Option<Expr>,
204}
205
206#[derive(Debug, Clone)]
207pub struct SelectStmt {
208 pub columns: Vec<SelectColumn>,
209 pub from: String,
210 pub from_alias: Option<String>,
211 pub joins: Vec<JoinClause>,
212 pub distinct: bool,
213 pub where_clause: Option<Expr>,
214 pub order_by: Vec<OrderByItem>,
215 pub limit: Option<Expr>,
216 pub offset: Option<Expr>,
217 pub group_by: Vec<Expr>,
218 pub having: Option<Expr>,
219}
220
221#[derive(Debug, Clone)]
222pub enum SetOp {
223 Union,
224 Intersect,
225 Except,
226}
227
228#[derive(Debug, Clone)]
229pub struct CompoundSelect {
230 pub op: SetOp,
231 pub all: bool,
232 pub left: Box<QueryBody>,
233 pub right: Box<QueryBody>,
234 pub order_by: Vec<OrderByItem>,
235 pub limit: Option<Expr>,
236 pub offset: Option<Expr>,
237}
238
239#[derive(Debug, Clone)]
240pub enum QueryBody {
241 Select(Box<SelectStmt>),
242 Compound(Box<CompoundSelect>),
243}
244
245#[derive(Debug, Clone)]
246pub struct CteDefinition {
247 pub name: String,
248 pub column_aliases: Vec<String>,
249 pub body: QueryBody,
250}
251
252#[derive(Debug, Clone)]
253pub struct SelectQuery {
254 pub ctes: Vec<CteDefinition>,
255 pub recursive: bool,
256 pub body: QueryBody,
257}
258
259#[derive(Debug, Clone)]
260pub struct UpdateStmt {
261 pub table: String,
262 pub assignments: Vec<(String, Expr)>,
263 pub where_clause: Option<Expr>,
264 pub returning: Option<Vec<SelectColumn>>,
265}
266
267#[derive(Debug, Clone)]
268pub struct DeleteStmt {
269 pub table: String,
270 pub where_clause: Option<Expr>,
271 pub returning: Option<Vec<SelectColumn>>,
272}
273
274#[derive(Debug, Clone)]
275pub enum SelectColumn {
276 AllColumns,
277 AllFromOld,
278 AllFromNew,
279 Expr { expr: Expr, alias: Option<String> },
280}
281
282#[derive(Debug, Clone)]
283pub struct OrderByItem {
284 pub expr: Expr,
285 pub descending: bool,
286 pub nulls_first: Option<bool>,
287}
288
289#[derive(Debug, Clone)]
290pub enum Expr {
291 Literal(Value),
292 Column(String),
293 QualifiedColumn {
294 table: String,
295 column: String,
296 },
297 BinaryOp {
298 left: Box<Expr>,
299 op: BinOp,
300 right: Box<Expr>,
301 },
302 UnaryOp {
303 op: UnaryOp,
304 expr: Box<Expr>,
305 },
306 IsNull(Box<Expr>),
307 IsNotNull(Box<Expr>),
308 Function {
309 name: String,
310 args: Vec<Expr>,
311 distinct: bool,
313 },
314 CountStar,
315 InSubquery {
316 expr: Box<Expr>,
317 subquery: Box<SelectStmt>,
318 negated: bool,
319 },
320 InList {
321 expr: Box<Expr>,
322 list: Vec<Expr>,
323 negated: bool,
324 },
325 Exists {
326 subquery: Box<SelectStmt>,
327 negated: bool,
328 },
329 ScalarSubquery(Box<SelectStmt>),
330 InSet {
331 expr: Box<Expr>,
332 values: rustc_hash::FxHashSet<Value>,
333 has_null: bool,
334 negated: bool,
335 },
336 Between {
337 expr: Box<Expr>,
338 low: Box<Expr>,
339 high: Box<Expr>,
340 negated: bool,
341 },
342 Like {
343 expr: Box<Expr>,
344 pattern: Box<Expr>,
345 escape: Option<Box<Expr>>,
346 negated: bool,
347 },
348 Case {
349 operand: Option<Box<Expr>>,
350 conditions: Vec<(Expr, Expr)>,
351 else_result: Option<Box<Expr>>,
352 },
353 Coalesce(Vec<Expr>),
354 Cast {
355 expr: Box<Expr>,
356 data_type: DataType,
357 },
358 Parameter(usize),
359 WindowFunction {
360 name: String,
361 args: Vec<Expr>,
362 spec: WindowSpec,
363 },
364}
365
366#[derive(Debug, Clone)]
367pub struct WindowSpec {
368 pub partition_by: Vec<Expr>,
369 pub order_by: Vec<OrderByItem>,
370 pub frame: Option<WindowFrame>,
371}
372
373#[derive(Debug, Clone)]
374pub struct WindowFrame {
375 pub units: WindowFrameUnits,
376 pub start: WindowFrameBound,
377 pub end: WindowFrameBound,
378}
379
380#[derive(Debug, Clone, Copy)]
381pub enum WindowFrameUnits {
382 Rows,
383 Range,
384 Groups,
385}
386
387#[derive(Debug, Clone)]
388pub enum WindowFrameBound {
389 UnboundedPreceding,
390 Preceding(Box<Expr>),
391 CurrentRow,
392 Following(Box<Expr>),
393 UnboundedFollowing,
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Eq)]
397pub enum BinOp {
398 Add,
399 Sub,
400 Mul,
401 Div,
402 Mod,
403 Eq,
404 NotEq,
405 Lt,
406 Gt,
407 LtEq,
408 GtEq,
409 And,
410 Or,
411 Concat,
412}
413
414#[derive(Debug, Clone, Copy, PartialEq, Eq)]
415pub enum UnaryOp {
416 Neg,
417 Not,
418}
419
420pub fn has_subquery(expr: &Expr) -> bool {
421 match expr {
422 Expr::InSubquery { .. } | Expr::Exists { .. } | Expr::ScalarSubquery(_) => true,
423 Expr::BinaryOp { left, right, .. } => has_subquery(left) || has_subquery(right),
424 Expr::UnaryOp { expr, .. } => has_subquery(expr),
425 Expr::IsNull(e) | Expr::IsNotNull(e) => has_subquery(e),
426 Expr::InList { expr, list, .. } => has_subquery(expr) || list.iter().any(has_subquery),
427 Expr::InSet { expr, .. } => has_subquery(expr),
428 Expr::Between {
429 expr, low, high, ..
430 } => has_subquery(expr) || has_subquery(low) || has_subquery(high),
431 Expr::Like {
432 expr,
433 pattern,
434 escape,
435 ..
436 } => {
437 has_subquery(expr)
438 || has_subquery(pattern)
439 || escape.as_ref().is_some_and(|e| has_subquery(e))
440 }
441 Expr::Case {
442 operand,
443 conditions,
444 else_result,
445 } => {
446 operand.as_ref().is_some_and(|e| has_subquery(e))
447 || conditions
448 .iter()
449 .any(|(c, r)| has_subquery(c) || has_subquery(r))
450 || else_result.as_ref().is_some_and(|e| has_subquery(e))
451 }
452 Expr::Coalesce(args) | Expr::Function { args, .. } => args.iter().any(has_subquery),
453 Expr::Cast { expr, .. } => has_subquery(expr),
454 _ => false,
455 }
456}
457
458pub fn parse_sql_expr(sql: &str) -> Result<Expr> {
461 let dialect = GenericDialect {};
462 let mut parser = Parser::new(&dialect)
463 .try_with_sql(sql)
464 .map_err(|e| SqlError::Parse(e.to_string()))?;
465 let sp_expr = parser
466 .parse_expr()
467 .map_err(|e| SqlError::Parse(e.to_string()))?;
468 convert_expr(&sp_expr)
469}
470
471pub fn parse_sql(sql: &str) -> Result<Statement> {
472 let dialect = GenericDialect {};
473 let stmts = Parser::parse_sql(&dialect, sql).map_err(|e| SqlError::Parse(e.to_string()))?;
474
475 if stmts.is_empty() {
476 return Err(SqlError::Parse("empty SQL".into()));
477 }
478 if stmts.len() > 1 {
479 return Err(SqlError::Unsupported("multiple statements".into()));
480 }
481
482 convert_statement(stmts.into_iter().next().unwrap())
483}
484
485pub fn parse_sql_multi(sql: &str) -> Result<Vec<Statement>> {
487 let dialect = GenericDialect {};
488 let stmts = Parser::parse_sql(&dialect, sql).map_err(|e| SqlError::Parse(e.to_string()))?;
489
490 if stmts.is_empty() {
491 return Err(SqlError::Parse("empty SQL".into()));
492 }
493
494 stmts.into_iter().map(convert_statement).collect()
495}
496
497pub fn count_params(stmt: &Statement) -> usize {
499 let mut max_idx = 0usize;
500 visit_exprs_stmt(stmt, &mut |e| {
501 if let Expr::Parameter(n) = e {
502 max_idx = max_idx.max(*n);
503 }
504 });
505 max_idx
506}
507
508fn visit_exprs_stmt(stmt: &Statement, visitor: &mut impl FnMut(&Expr)) {
509 match stmt {
510 Statement::Select(sq) => {
511 for cte in &sq.ctes {
512 visit_exprs_query_body(&cte.body, visitor);
513 }
514 visit_exprs_query_body(&sq.body, visitor);
515 }
516 Statement::Insert(ins) => match &ins.source {
517 InsertSource::Values(rows) => {
518 for row in rows {
519 for e in row {
520 visit_expr(e, visitor);
521 }
522 }
523 }
524 InsertSource::Select(sq) => {
525 for cte in &sq.ctes {
526 visit_exprs_query_body(&cte.body, visitor);
527 }
528 visit_exprs_query_body(&sq.body, visitor);
529 }
530 },
531 Statement::Update(upd) => {
532 for (_, e) in &upd.assignments {
533 visit_expr(e, visitor);
534 }
535 if let Some(w) = &upd.where_clause {
536 visit_expr(w, visitor);
537 }
538 }
539 Statement::Delete(del) => {
540 if let Some(w) = &del.where_clause {
541 visit_expr(w, visitor);
542 }
543 }
544 Statement::Explain(inner) => visit_exprs_stmt(inner, visitor),
545 _ => {}
546 }
547}
548
549fn visit_exprs_query_body(body: &QueryBody, visitor: &mut impl FnMut(&Expr)) {
550 match body {
551 QueryBody::Select(sel) => visit_exprs_select(sel, visitor),
552 QueryBody::Compound(comp) => {
553 visit_exprs_query_body(&comp.left, visitor);
554 visit_exprs_query_body(&comp.right, visitor);
555 for o in &comp.order_by {
556 visit_expr(&o.expr, visitor);
557 }
558 if let Some(l) = &comp.limit {
559 visit_expr(l, visitor);
560 }
561 if let Some(o) = &comp.offset {
562 visit_expr(o, visitor);
563 }
564 }
565 }
566}
567
568fn visit_exprs_select(sel: &SelectStmt, visitor: &mut impl FnMut(&Expr)) {
569 for col in &sel.columns {
570 if let SelectColumn::Expr { expr, .. } = col {
571 visit_expr(expr, visitor);
572 }
573 }
574 for j in &sel.joins {
575 if let Some(on) = &j.on_clause {
576 visit_expr(on, visitor);
577 }
578 }
579 if let Some(w) = &sel.where_clause {
580 visit_expr(w, visitor);
581 }
582 for o in &sel.order_by {
583 visit_expr(&o.expr, visitor);
584 }
585 if let Some(l) = &sel.limit {
586 visit_expr(l, visitor);
587 }
588 if let Some(o) = &sel.offset {
589 visit_expr(o, visitor);
590 }
591 for g in &sel.group_by {
592 visit_expr(g, visitor);
593 }
594 if let Some(h) = &sel.having {
595 visit_expr(h, visitor);
596 }
597}
598
599fn visit_expr(expr: &Expr, visitor: &mut impl FnMut(&Expr)) {
600 visitor(expr);
601 match expr {
602 Expr::BinaryOp { left, right, .. } => {
603 visit_expr(left, visitor);
604 visit_expr(right, visitor);
605 }
606 Expr::UnaryOp { expr: e, .. } | Expr::IsNull(e) | Expr::IsNotNull(e) => {
607 visit_expr(e, visitor);
608 }
609 Expr::Function { args, .. } | Expr::Coalesce(args) => {
610 for a in args {
611 visit_expr(a, visitor);
612 }
613 }
614 Expr::InSubquery {
615 expr: e, subquery, ..
616 } => {
617 visit_expr(e, visitor);
618 visit_exprs_select(subquery, visitor);
619 }
620 Expr::InList { expr: e, list, .. } => {
621 visit_expr(e, visitor);
622 for l in list {
623 visit_expr(l, visitor);
624 }
625 }
626 Expr::Exists { subquery, .. } => visit_exprs_select(subquery, visitor),
627 Expr::ScalarSubquery(sq) => visit_exprs_select(sq, visitor),
628 Expr::InSet { expr: e, .. } => visit_expr(e, visitor),
629 Expr::Between {
630 expr: e, low, high, ..
631 } => {
632 visit_expr(e, visitor);
633 visit_expr(low, visitor);
634 visit_expr(high, visitor);
635 }
636 Expr::Like {
637 expr: e,
638 pattern,
639 escape,
640 ..
641 } => {
642 visit_expr(e, visitor);
643 visit_expr(pattern, visitor);
644 if let Some(esc) = escape {
645 visit_expr(esc, visitor);
646 }
647 }
648 Expr::Case {
649 operand,
650 conditions,
651 else_result,
652 } => {
653 if let Some(op) = operand {
654 visit_expr(op, visitor);
655 }
656 for (cond, then) in conditions {
657 visit_expr(cond, visitor);
658 visit_expr(then, visitor);
659 }
660 if let Some(el) = else_result {
661 visit_expr(el, visitor);
662 }
663 }
664 Expr::Cast { expr: e, .. } => visit_expr(e, visitor),
665 Expr::WindowFunction { args, spec, .. } => {
666 for a in args {
667 visit_expr(a, visitor);
668 }
669 for p in &spec.partition_by {
670 visit_expr(p, visitor);
671 }
672 for o in &spec.order_by {
673 visit_expr(&o.expr, visitor);
674 }
675 if let Some(ref frame) = spec.frame {
676 if let WindowFrameBound::Preceding(e) | WindowFrameBound::Following(e) =
677 &frame.start
678 {
679 visit_expr(e, visitor);
680 }
681 if let WindowFrameBound::Preceding(e) | WindowFrameBound::Following(e) = &frame.end
682 {
683 visit_expr(e, visitor);
684 }
685 }
686 }
687 Expr::Literal(_)
688 | Expr::Column(_)
689 | Expr::QualifiedColumn { .. }
690 | Expr::CountStar
691 | Expr::Parameter(_) => {}
692 }
693}
694
695fn convert_statement(stmt: sp::Statement) -> Result<Statement> {
696 match stmt {
697 sp::Statement::CreateTable(ct) => convert_create_table(ct),
698 sp::Statement::CreateIndex(ci) => convert_create_index(ci),
699 sp::Statement::Drop {
700 object_type: sp::ObjectType::Table,
701 if_exists,
702 names,
703 ..
704 } => {
705 if names.len() != 1 {
706 return Err(SqlError::Unsupported("multi-table DROP".into()));
707 }
708 Ok(Statement::DropTable(DropTableStmt {
709 name: object_name_to_string(&names[0]),
710 if_exists,
711 }))
712 }
713 sp::Statement::Drop {
714 object_type: sp::ObjectType::Index,
715 if_exists,
716 names,
717 ..
718 } => {
719 if names.len() != 1 {
720 return Err(SqlError::Unsupported("multi-index DROP".into()));
721 }
722 Ok(Statement::DropIndex(DropIndexStmt {
723 index_name: object_name_to_string(&names[0]),
724 if_exists,
725 }))
726 }
727 sp::Statement::CreateView(cv) => convert_create_view(cv),
728 sp::Statement::Drop {
729 object_type: sp::ObjectType::View,
730 if_exists,
731 names,
732 ..
733 } => {
734 if names.len() != 1 {
735 return Err(SqlError::Unsupported("multi-view DROP".into()));
736 }
737 Ok(Statement::DropView(DropViewStmt {
738 name: object_name_to_string(&names[0]),
739 if_exists,
740 }))
741 }
742 sp::Statement::AlterTable(at) => convert_alter_table(at),
743 sp::Statement::Insert(insert) => convert_insert(insert),
744 sp::Statement::Query(query) => convert_query(*query),
745 sp::Statement::Update(update) => convert_update(update),
746 sp::Statement::Delete(delete) => convert_delete(delete),
747 sp::Statement::StartTransaction { .. } => Ok(Statement::Begin),
748 sp::Statement::Commit { chain: true, .. } => {
749 Err(SqlError::Unsupported("COMMIT AND CHAIN".into()))
750 }
751 sp::Statement::Commit { .. } => Ok(Statement::Commit),
752 sp::Statement::Rollback { chain: true, .. } => {
753 Err(SqlError::Unsupported("ROLLBACK AND CHAIN".into()))
754 }
755 sp::Statement::Rollback {
756 savepoint: Some(name),
757 ..
758 } => Ok(Statement::RollbackTo(name.value.to_ascii_lowercase())),
759 sp::Statement::Rollback { .. } => Ok(Statement::Rollback),
760 sp::Statement::Savepoint { name } => {
761 Ok(Statement::Savepoint(name.value.to_ascii_lowercase()))
762 }
763 sp::Statement::ReleaseSavepoint { name } => {
764 Ok(Statement::ReleaseSavepoint(name.value.to_ascii_lowercase()))
765 }
766 sp::Statement::Set(sp::Set::SetTimeZone { value, .. }) => {
767 let zone = match value {
769 sp::Expr::Value(v) => match &v.value {
770 sp::Value::SingleQuotedString(s) => s.clone(),
771 sp::Value::DoubleQuotedString(s) => s.clone(),
772 other => other.to_string(),
773 },
774 sp::Expr::Identifier(ident) => ident.value.clone(),
775 other => {
776 return Err(SqlError::Parse(format!(
777 "SET TIME ZONE expects a string literal or identifier, got: {other}"
778 )))
779 }
780 };
781 Ok(Statement::SetTimezone(zone))
782 }
783 sp::Statement::Explain {
784 statement, analyze, ..
785 } => {
786 if analyze {
787 return Err(SqlError::Unsupported("EXPLAIN ANALYZE".into()));
788 }
789 let inner = convert_statement(*statement)?;
790 Ok(Statement::Explain(Box::new(inner)))
791 }
792 _ => Err(SqlError::Unsupported(format!("statement type: {}", stmt))),
793 }
794}
795
796fn convert_column_def(
799 col_def: &sp::ColumnDef,
800) -> Result<(ColumnSpec, Option<ForeignKeyDef>, bool, bool)> {
801 let col_name = col_def.name.value.clone();
802 let data_type = convert_data_type(&col_def.data_type)?;
803 let mut nullable = true;
804 let mut is_primary_key = false;
805 let mut is_unique = false;
806 let mut default_expr = None;
807 let mut default_sql = None;
808 let mut check_expr = None;
809 let mut check_sql = None;
810 let mut check_name = None;
811 let mut generated_expr = None;
812 let mut generated_sql = None;
813 let mut generated_kind = None;
814 let mut fk_def = None;
815
816 for opt in &col_def.options {
817 match &opt.option {
818 sp::ColumnOption::NotNull => nullable = false,
819 sp::ColumnOption::Null => nullable = true,
820 sp::ColumnOption::PrimaryKey(_) => {
821 is_primary_key = true;
822 nullable = false;
823 }
824 sp::ColumnOption::Unique(_) => is_unique = true,
825 sp::ColumnOption::Default(expr) => {
826 default_sql = Some(expr.to_string());
827 default_expr = Some(convert_expr(expr)?);
828 }
829 sp::ColumnOption::Check(check) => {
830 check_sql = Some(check.expr.to_string());
831 let converted = convert_expr(&check.expr)?;
832 if has_subquery(&converted) {
833 return Err(SqlError::Unsupported("subquery in CHECK constraint".into()));
834 }
835 check_expr = Some(converted);
836 check_name = check.name.as_ref().map(|n| n.value.clone());
837 }
838 sp::ColumnOption::ForeignKey(fk) => {
839 convert_fk_actions(&fk.on_delete, &fk.on_update)?;
840 let ftable = object_name_to_string(&fk.foreign_table).to_ascii_lowercase();
841 let referred: Vec<String> = fk
842 .referred_columns
843 .iter()
844 .map(|i| i.value.to_ascii_lowercase())
845 .collect();
846 fk_def = Some(ForeignKeyDef {
847 name: fk.name.as_ref().map(|n| n.value.clone()),
848 columns: vec![col_name.to_ascii_lowercase()],
849 foreign_table: ftable,
850 referred_columns: referred,
851 });
852 }
853 sp::ColumnOption::Generated {
854 generation_expr,
855 generation_expr_mode,
856 sequence_options: _,
857 ..
858 } => {
859 let Some(expr) = generation_expr else {
860 return Err(SqlError::Unsupported(
861 "identity columns not yet supported; use INTEGER PRIMARY KEY for autoincrement".into(),
862 ));
863 };
864 let mode = generation_expr_mode.unwrap_or(sp::GeneratedExpressionMode::Virtual);
865 let converted = convert_expr(expr)?;
866 reject_aggregate_or_window(expr, "GENERATED")?;
867 if has_subquery(&converted) {
868 return Err(SqlError::Unsupported(
869 "subquery in GENERATED expression".into(),
870 ));
871 }
872 reject_volatile_in_generated(&converted)?;
873 generated_sql = Some(expr.to_string());
874 generated_expr = Some(converted);
875 generated_kind = Some(match mode {
876 sp::GeneratedExpressionMode::Stored => GeneratedKind::Stored,
877 sp::GeneratedExpressionMode::Virtual => GeneratedKind::Virtual,
878 });
879 }
880 _ => {}
881 }
882 }
883
884 if generated_kind.is_some() {
885 if default_expr.is_some() {
886 return Err(SqlError::Unsupported(
887 "DEFAULT and GENERATED cannot be combined".into(),
888 ));
889 }
890 if is_primary_key {
891 return Err(SqlError::Unsupported(
892 "GENERATED column cannot be PRIMARY KEY".into(),
893 ));
894 }
895 }
896
897 let spec = ColumnSpec {
898 name: col_name,
899 data_type,
900 nullable,
901 is_primary_key,
902 default_expr,
903 default_sql,
904 check_expr,
905 check_sql,
906 check_name,
907 generated_expr,
908 generated_sql,
909 generated_kind,
910 };
911 Ok((spec, fk_def, is_primary_key, is_unique))
912}
913
914fn reject_volatile_in_generated(expr: &Expr) -> Result<()> {
915 fn walk(e: &Expr) -> Result<()> {
916 match e {
917 Expr::Function { name, args, .. } => {
918 let upper = name.to_ascii_uppercase();
919 if matches!(
920 upper.as_str(),
921 "RANDOM"
922 | "NOW"
923 | "CURRENT_TIMESTAMP"
924 | "CURRENT_DATE"
925 | "CURRENT_TIME"
926 | "CLOCK_TIMESTAMP"
927 | "STATEMENT_TIMESTAMP"
928 | "TRANSACTION_TIMESTAMP"
929 | "LOCALTIMESTAMP"
930 | "LOCALTIME"
931 ) {
932 return Err(SqlError::Unsupported(format!(
933 "volatile function {name}() not allowed in GENERATED expression"
934 )));
935 }
936 for a in args {
937 walk(a)?;
938 }
939 Ok(())
940 }
941 Expr::BinaryOp { left, right, .. } => {
942 walk(left)?;
943 walk(right)
944 }
945 Expr::UnaryOp { expr, .. } => walk(expr),
946 Expr::Cast { expr, .. } => walk(expr),
947 Expr::Case {
948 operand,
949 conditions,
950 else_result,
951 } => {
952 if let Some(o) = operand {
953 walk(o)?;
954 }
955 for (cond, res) in conditions {
956 walk(cond)?;
957 walk(res)?;
958 }
959 if let Some(e) = else_result {
960 walk(e)?;
961 }
962 Ok(())
963 }
964 Expr::Coalesce(items) => items.iter().try_for_each(walk),
965 _ => Ok(()),
966 }
967 }
968 walk(expr)
969}
970
971fn convert_create_table(ct: sp::CreateTable) -> Result<Statement> {
972 let name = object_name_to_string(&ct.name);
973 let if_not_exists = ct.if_not_exists;
974
975 let mut columns = Vec::new();
976 let mut inline_pk: Vec<String> = Vec::new();
977 let mut foreign_keys: Vec<ForeignKeyDef> = Vec::new();
978 let mut unique_indices: Vec<UniqueIndexDef> = Vec::new();
979
980 for col_def in &ct.columns {
981 let (spec, fk_def, was_pk, was_unique) = convert_column_def(col_def)?;
982 if was_pk {
983 inline_pk.push(spec.name.clone());
984 }
985 if let Some(fk) = fk_def {
986 foreign_keys.push(fk);
987 }
988 if was_unique && !was_pk {
989 unique_indices.push(UniqueIndexDef {
990 name: None,
991 columns: vec![spec.name.to_ascii_lowercase()],
992 });
993 }
994 columns.push(spec);
995 }
996
997 let mut check_constraints: Vec<TableCheckConstraint> = Vec::new();
998
999 for constraint in &ct.constraints {
1000 match constraint {
1001 sp::TableConstraint::PrimaryKey(pk_constraint) => {
1002 for idx_col in &pk_constraint.columns {
1003 let col_name = match &idx_col.column.expr {
1004 sp::Expr::Identifier(ident) => ident.value.clone(),
1005 _ => continue,
1006 };
1007 if !inline_pk.contains(&col_name) {
1008 inline_pk.push(col_name.clone());
1009 }
1010 if let Some(col) = columns.iter_mut().find(|c| c.name == col_name) {
1011 col.nullable = false;
1012 col.is_primary_key = true;
1013 }
1014 }
1015 }
1016 sp::TableConstraint::Check(check) => {
1017 let sql = check.expr.to_string();
1018 let converted = convert_expr(&check.expr)?;
1019 if has_subquery(&converted) {
1020 return Err(SqlError::Unsupported("subquery in CHECK constraint".into()));
1021 }
1022 check_constraints.push(TableCheckConstraint {
1023 name: check.name.as_ref().map(|n| n.value.clone()),
1024 expr: converted,
1025 sql,
1026 });
1027 }
1028 sp::TableConstraint::ForeignKey(fk) => {
1029 convert_fk_actions(&fk.on_delete, &fk.on_update)?;
1030 let cols: Vec<String> = fk
1031 .columns
1032 .iter()
1033 .map(|i| i.value.to_ascii_lowercase())
1034 .collect();
1035 let ftable = object_name_to_string(&fk.foreign_table).to_ascii_lowercase();
1036 let referred: Vec<String> = fk
1037 .referred_columns
1038 .iter()
1039 .map(|i| i.value.to_ascii_lowercase())
1040 .collect();
1041 foreign_keys.push(ForeignKeyDef {
1042 name: fk.name.as_ref().map(|n| n.value.clone()),
1043 columns: cols,
1044 foreign_table: ftable,
1045 referred_columns: referred,
1046 });
1047 }
1048 sp::TableConstraint::Unique(u) => {
1049 let cols: Vec<String> = u
1050 .columns
1051 .iter()
1052 .filter_map(|idx_col| match &idx_col.column.expr {
1053 sp::Expr::Identifier(ident) => Some(ident.value.to_ascii_lowercase()),
1054 _ => None,
1055 })
1056 .collect();
1057 if !cols.is_empty() {
1058 unique_indices.push(UniqueIndexDef {
1059 name: u.name.as_ref().map(|n| n.value.clone()),
1060 columns: cols,
1061 });
1062 }
1063 }
1064 _ => {}
1065 }
1066 }
1067
1068 Ok(Statement::CreateTable(CreateTableStmt {
1069 name,
1070 columns,
1071 primary_key: inline_pk,
1072 if_not_exists,
1073 check_constraints,
1074 foreign_keys,
1075 unique_indices,
1076 }))
1077}
1078
1079fn convert_alter_table(at: sp::AlterTable) -> Result<Statement> {
1080 let table = object_name_to_string(&at.name);
1081 if at.operations.len() != 1 {
1082 return Err(SqlError::Unsupported(
1083 "ALTER TABLE with multiple operations".into(),
1084 ));
1085 }
1086 let op = match at.operations.into_iter().next().unwrap() {
1087 sp::AlterTableOperation::AddColumn {
1088 column_def,
1089 if_not_exists,
1090 ..
1091 } => {
1092 let (spec, fk, _was_pk, _was_unique) = convert_column_def(&column_def)?;
1093 AlterTableOp::AddColumn {
1094 column: Box::new(spec),
1095 foreign_key: fk,
1096 if_not_exists,
1097 }
1098 }
1099 sp::AlterTableOperation::DropColumn {
1100 column_names,
1101 if_exists,
1102 ..
1103 } => {
1104 if column_names.len() != 1 {
1105 return Err(SqlError::Unsupported(
1106 "DROP COLUMN with multiple columns".into(),
1107 ));
1108 }
1109 AlterTableOp::DropColumn {
1110 name: column_names.into_iter().next().unwrap().value,
1111 if_exists,
1112 }
1113 }
1114 sp::AlterTableOperation::RenameColumn {
1115 old_column_name,
1116 new_column_name,
1117 } => AlterTableOp::RenameColumn {
1118 old_name: old_column_name.value,
1119 new_name: new_column_name.value,
1120 },
1121 sp::AlterTableOperation::RenameTable { table_name } => {
1122 let new_name = match table_name {
1123 sp::RenameTableNameKind::To(name) | sp::RenameTableNameKind::As(name) => {
1124 object_name_to_string(&name)
1125 }
1126 };
1127 AlterTableOp::RenameTable { new_name }
1128 }
1129 other => {
1130 return Err(SqlError::Unsupported(format!(
1131 "ALTER TABLE operation: {other}"
1132 )));
1133 }
1134 };
1135 Ok(Statement::AlterTable(Box::new(AlterTableStmt {
1136 table,
1137 op,
1138 })))
1139}
1140
1141fn convert_fk_actions(
1142 on_delete: &Option<sp::ReferentialAction>,
1143 on_update: &Option<sp::ReferentialAction>,
1144) -> Result<()> {
1145 for action in [on_delete, on_update] {
1146 match action {
1147 None
1148 | Some(sp::ReferentialAction::Restrict)
1149 | Some(sp::ReferentialAction::NoAction) => {}
1150 Some(other) => {
1151 return Err(SqlError::Unsupported(format!(
1152 "FOREIGN KEY action: {other}"
1153 )));
1154 }
1155 }
1156 }
1157 Ok(())
1158}
1159
1160fn convert_create_index(ci: sp::CreateIndex) -> Result<Statement> {
1161 let index_name = ci
1162 .name
1163 .as_ref()
1164 .map(object_name_to_string)
1165 .ok_or_else(|| SqlError::Parse("index name required".into()))?;
1166
1167 let table_name = object_name_to_string(&ci.table_name);
1168
1169 let columns: Vec<String> = ci
1170 .columns
1171 .iter()
1172 .map(|idx_col| match &idx_col.column.expr {
1173 sp::Expr::Identifier(ident) => Ok(ident.value.clone()),
1174 other => Err(SqlError::Unsupported(format!("expression index: {other}"))),
1175 })
1176 .collect::<Result<_>>()?;
1177
1178 if columns.is_empty() {
1179 return Err(SqlError::Parse(
1180 "index must have at least one column".into(),
1181 ));
1182 }
1183
1184 Ok(Statement::CreateIndex(CreateIndexStmt {
1185 index_name,
1186 table_name,
1187 columns,
1188 unique: ci.unique,
1189 if_not_exists: ci.if_not_exists,
1190 }))
1191}
1192
1193fn convert_create_view(cv: sp::CreateView) -> Result<Statement> {
1194 let name = object_name_to_string(&cv.name);
1195
1196 if cv.materialized {
1197 return Err(SqlError::Unsupported("MATERIALIZED VIEW".into()));
1198 }
1199
1200 let sql = cv.query.to_string();
1201
1202 let dialect = GenericDialect {};
1203 let test = Parser::parse_sql(&dialect, &sql).map_err(|e| SqlError::Parse(e.to_string()))?;
1204 if test.is_empty() {
1205 return Err(SqlError::Parse("empty view definition".into()));
1206 }
1207 match &test[0] {
1208 sp::Statement::Query(_) => {}
1209 _ => {
1210 return Err(SqlError::Parse(
1211 "view body must be a SELECT statement".into(),
1212 ))
1213 }
1214 }
1215
1216 let column_aliases: Vec<String> = cv
1217 .columns
1218 .iter()
1219 .map(|c| c.name.value.to_ascii_lowercase())
1220 .collect();
1221
1222 Ok(Statement::CreateView(CreateViewStmt {
1223 name,
1224 sql,
1225 column_aliases,
1226 or_replace: cv.or_replace,
1227 if_not_exists: cv.if_not_exists,
1228 }))
1229}
1230
1231fn convert_insert(insert: sp::Insert) -> Result<Statement> {
1232 let table = match &insert.table {
1233 sp::TableObject::TableName(name) => object_name_to_string(name).to_ascii_lowercase(),
1234 _ => return Err(SqlError::Unsupported("INSERT into non-table object".into())),
1235 };
1236
1237 let columns: Vec<String> = insert
1238 .columns
1239 .iter()
1240 .map(|c| c.value.to_ascii_lowercase())
1241 .collect();
1242
1243 let query = insert
1244 .source
1245 .ok_or_else(|| SqlError::Parse("INSERT requires VALUES or SELECT".into()))?;
1246
1247 let source = match *query.body {
1248 sp::SetExpr::Values(sp::Values { rows, .. }) => {
1249 let mut result = Vec::new();
1250 for row in rows {
1251 let mut exprs = Vec::new();
1252 for expr in row {
1253 exprs.push(convert_expr(&expr)?);
1254 }
1255 result.push(exprs);
1256 }
1257 InsertSource::Values(result)
1258 }
1259 _ => {
1260 let (ctes, recursive) = if let Some(ref with) = query.with {
1261 convert_with(with)?
1262 } else {
1263 (vec![], false)
1264 };
1265 let body = convert_query_body(&query)?;
1266 InsertSource::Select(Box::new(SelectQuery {
1267 ctes,
1268 recursive,
1269 body,
1270 }))
1271 }
1272 };
1273
1274 let on_conflict = insert.on.as_ref().map(convert_on_insert).transpose()?;
1275 let returning = convert_returning(insert.returning.as_deref())?;
1276
1277 Ok(Statement::Insert(InsertStmt {
1278 table,
1279 columns,
1280 source,
1281 on_conflict,
1282 returning,
1283 }))
1284}
1285
1286fn convert_on_insert(on: &sp::OnInsert) -> Result<OnConflictClause> {
1287 match on {
1288 sp::OnInsert::OnConflict(oc) => {
1289 let target = oc
1290 .conflict_target
1291 .as_ref()
1292 .map(convert_conflict_target)
1293 .transpose()?;
1294 let action = convert_on_conflict_action(&oc.action)?;
1295 Ok(OnConflictClause { target, action })
1296 }
1297 sp::OnInsert::DuplicateKeyUpdate(_) => Err(SqlError::Parse(
1298 "ON DUPLICATE KEY UPDATE is MySQL-specific; use ON CONFLICT".into(),
1299 )),
1300 _ => Err(SqlError::Parse("unsupported ON INSERT clause".into())),
1301 }
1302}
1303
1304fn convert_conflict_target(target: &sp::ConflictTarget) -> Result<ConflictTarget> {
1305 match target {
1306 sp::ConflictTarget::Columns(cols) => Ok(ConflictTarget::Columns(
1307 cols.iter().map(|c| c.value.to_ascii_lowercase()).collect(),
1308 )),
1309 sp::ConflictTarget::OnConstraint(name) => {
1310 if name.0.len() > 1 {
1311 return Err(SqlError::Parse(
1312 "qualified constraint names not supported".into(),
1313 ));
1314 }
1315 Ok(ConflictTarget::Constraint(
1316 object_name_to_string(name).to_ascii_lowercase(),
1317 ))
1318 }
1319 }
1320}
1321
1322fn convert_on_conflict_action(action: &sp::OnConflictAction) -> Result<OnConflictAction> {
1323 match action {
1324 sp::OnConflictAction::DoNothing => Ok(OnConflictAction::DoNothing),
1325 sp::OnConflictAction::DoUpdate(du) => {
1326 let assignments = du
1327 .assignments
1328 .iter()
1329 .map(|a| {
1330 let col = match &a.target {
1331 sp::AssignmentTarget::ColumnName(name) => {
1332 object_name_to_string(name).to_ascii_lowercase()
1333 }
1334 _ => {
1335 return Err(SqlError::Unsupported(
1336 "tuple assignment in ON CONFLICT".into(),
1337 ))
1338 }
1339 };
1340 let expr = convert_expr(&a.value)?;
1341 Ok((col, expr))
1342 })
1343 .collect::<Result<_>>()?;
1344 let where_clause = du.selection.as_ref().map(convert_expr).transpose()?;
1345 Ok(OnConflictAction::DoUpdate {
1346 assignments,
1347 where_clause,
1348 })
1349 }
1350 }
1351}
1352
1353fn convert_select_body(select: &sp::Select) -> Result<SelectStmt> {
1354 let distinct = match &select.distinct {
1355 Some(sp::Distinct::Distinct) => true,
1356 Some(sp::Distinct::On(_)) => {
1357 return Err(SqlError::Unsupported("DISTINCT ON".into()));
1358 }
1359 _ => false,
1360 };
1361
1362 let (from, from_alias, joins) = if select.from.is_empty() {
1363 (String::new(), None, vec![])
1364 } else if select.from.len() == 1 {
1365 let table_with_joins = &select.from[0];
1366 let (name, alias) = match &table_with_joins.relation {
1367 sp::TableFactor::Table { name, alias, .. } => {
1368 let table_name = object_name_to_string(name);
1369 let alias_str = alias.as_ref().map(|a| a.name.value.clone());
1370 (table_name, alias_str)
1371 }
1372 _ => return Err(SqlError::Unsupported("non-table FROM source".into())),
1373 };
1374 let j = table_with_joins
1375 .joins
1376 .iter()
1377 .map(convert_join)
1378 .collect::<Result<Vec<_>>>()?;
1379 (name, alias, j)
1380 } else {
1381 return Err(SqlError::Unsupported("comma-separated FROM tables".into()));
1382 };
1383
1384 let columns: Vec<SelectColumn> = select
1385 .projection
1386 .iter()
1387 .map(convert_select_item)
1388 .collect::<Result<_>>()?;
1389
1390 let where_clause = select.selection.as_ref().map(convert_expr).transpose()?;
1391
1392 let group_by = match &select.group_by {
1393 sp::GroupByExpr::Expressions(exprs, _) => {
1394 exprs.iter().map(convert_expr).collect::<Result<_>>()?
1395 }
1396 sp::GroupByExpr::All(_) => {
1397 return Err(SqlError::Unsupported("GROUP BY ALL".into()));
1398 }
1399 };
1400
1401 let having = select.having.as_ref().map(convert_expr).transpose()?;
1402
1403 Ok(SelectStmt {
1404 columns,
1405 from,
1406 from_alias,
1407 joins,
1408 distinct,
1409 where_clause,
1410 order_by: vec![],
1411 limit: None,
1412 offset: None,
1413 group_by,
1414 having,
1415 })
1416}
1417
1418fn convert_set_expr(set_expr: &sp::SetExpr) -> Result<QueryBody> {
1419 match set_expr {
1420 sp::SetExpr::Select(sel) => Ok(QueryBody::Select(Box::new(convert_select_body(sel)?))),
1421 sp::SetExpr::SetOperation {
1422 op,
1423 set_quantifier,
1424 left,
1425 right,
1426 } => {
1427 let set_op = match op {
1428 sp::SetOperator::Union => SetOp::Union,
1429 sp::SetOperator::Intersect => SetOp::Intersect,
1430 sp::SetOperator::Except | sp::SetOperator::Minus => SetOp::Except,
1431 };
1432 let all = match set_quantifier {
1433 sp::SetQuantifier::All => true,
1434 sp::SetQuantifier::None | sp::SetQuantifier::Distinct => false,
1435 _ => {
1436 return Err(SqlError::Unsupported("BY NAME set operations".into()));
1437 }
1438 };
1439 Ok(QueryBody::Compound(Box::new(CompoundSelect {
1440 op: set_op,
1441 all,
1442 left: Box::new(convert_set_expr(left)?),
1443 right: Box::new(convert_set_expr(right)?),
1444 order_by: vec![],
1445 limit: None,
1446 offset: None,
1447 })))
1448 }
1449 _ => Err(SqlError::Unsupported("unsupported set expression".into())),
1450 }
1451}
1452
1453fn convert_query_body(query: &sp::Query) -> Result<QueryBody> {
1454 let mut body = convert_set_expr(&query.body)?;
1455
1456 let order_by = if let Some(ref ob) = query.order_by {
1457 match &ob.kind {
1458 sp::OrderByKind::Expressions(exprs) => exprs
1459 .iter()
1460 .map(convert_order_by_expr)
1461 .collect::<Result<_>>()?,
1462 sp::OrderByKind::All { .. } => {
1463 return Err(SqlError::Unsupported("ORDER BY ALL".into()));
1464 }
1465 }
1466 } else {
1467 vec![]
1468 };
1469
1470 let (limit, offset) = match &query.limit_clause {
1471 Some(sp::LimitClause::LimitOffset { limit, offset, .. }) => {
1472 let l = limit.as_ref().map(convert_expr).transpose()?;
1473 let o = offset
1474 .as_ref()
1475 .map(|o| convert_expr(&o.value))
1476 .transpose()?;
1477 (l, o)
1478 }
1479 Some(sp::LimitClause::OffsetCommaLimit { limit, offset }) => {
1480 let l = Some(convert_expr(limit)?);
1481 let o = Some(convert_expr(offset)?);
1482 (l, o)
1483 }
1484 None => (None, None),
1485 };
1486
1487 match &mut body {
1488 QueryBody::Select(sel) => {
1489 sel.order_by = order_by;
1490 sel.limit = limit;
1491 sel.offset = offset;
1492 }
1493 QueryBody::Compound(comp) => {
1494 comp.order_by = order_by;
1495 comp.limit = limit;
1496 comp.offset = offset;
1497 }
1498 }
1499
1500 Ok(body)
1501}
1502
1503fn convert_subquery(query: &sp::Query) -> Result<SelectStmt> {
1504 if query.with.is_some() {
1505 return Err(SqlError::Unsupported("CTEs in subqueries".into()));
1506 }
1507 match convert_query_body(query)? {
1508 QueryBody::Select(s) => Ok(*s),
1509 QueryBody::Compound(_) => Err(SqlError::Unsupported(
1510 "UNION/INTERSECT/EXCEPT in subqueries".into(),
1511 )),
1512 }
1513}
1514
1515fn convert_with(with: &sp::With) -> Result<(Vec<CteDefinition>, bool)> {
1516 let mut names = rustc_hash::FxHashSet::default();
1517 let mut ctes = Vec::new();
1518 for cte in &with.cte_tables {
1519 let name = cte.alias.name.value.to_ascii_lowercase();
1520 if !names.insert(name.clone()) {
1521 return Err(SqlError::DuplicateCteName(name));
1522 }
1523 let column_aliases: Vec<String> = cte
1524 .alias
1525 .columns
1526 .iter()
1527 .map(|c| c.name.value.to_ascii_lowercase())
1528 .collect();
1529 let body = convert_query_body(&cte.query)?;
1530 ctes.push(CteDefinition {
1531 name,
1532 column_aliases,
1533 body,
1534 });
1535 }
1536 Ok((ctes, with.recursive))
1537}
1538
1539fn convert_query(query: sp::Query) -> Result<Statement> {
1540 let (ctes, recursive) = if let Some(ref with) = query.with {
1541 convert_with(with)?
1542 } else {
1543 (vec![], false)
1544 };
1545 let body = convert_query_body(&query)?;
1546 Ok(Statement::Select(Box::new(SelectQuery {
1547 ctes,
1548 recursive,
1549 body,
1550 })))
1551}
1552
1553fn convert_join(join: &sp::Join) -> Result<JoinClause> {
1554 let (join_type, constraint) = match &join.join_operator {
1555 sp::JoinOperator::Inner(c) => (JoinType::Inner, Some(c)),
1556 sp::JoinOperator::Join(c) => (JoinType::Inner, Some(c)),
1557 sp::JoinOperator::CrossJoin(c) => (JoinType::Cross, Some(c)),
1558 sp::JoinOperator::Left(c) => (JoinType::Left, Some(c)),
1559 sp::JoinOperator::LeftSemi(c) => (JoinType::Left, Some(c)),
1560 sp::JoinOperator::LeftAnti(c) => (JoinType::Left, Some(c)),
1561 sp::JoinOperator::Right(c) => (JoinType::Right, Some(c)),
1562 sp::JoinOperator::RightSemi(c) => (JoinType::Right, Some(c)),
1563 sp::JoinOperator::RightAnti(c) => (JoinType::Right, Some(c)),
1564 other => return Err(SqlError::Unsupported(format!("join type: {other:?}"))),
1565 };
1566
1567 let (name, alias) = match &join.relation {
1568 sp::TableFactor::Table { name, alias, .. } => {
1569 let table_name = object_name_to_string(name);
1570 let alias_str = alias.as_ref().map(|a| a.name.value.clone());
1571 (table_name, alias_str)
1572 }
1573 _ => return Err(SqlError::Unsupported("non-table JOIN source".into())),
1574 };
1575
1576 let on_clause = match constraint {
1577 Some(sp::JoinConstraint::On(expr)) => Some(convert_expr(expr)?),
1578 Some(sp::JoinConstraint::None) | None => None,
1579 Some(other) => return Err(SqlError::Unsupported(format!("join constraint: {other:?}"))),
1580 };
1581
1582 Ok(JoinClause {
1583 join_type,
1584 table: TableRef { name, alias },
1585 on_clause,
1586 })
1587}
1588
1589fn convert_update(update: sp::Update) -> Result<Statement> {
1590 let table = match &update.table.relation {
1591 sp::TableFactor::Table { name, .. } => object_name_to_string(name),
1592 _ => return Err(SqlError::Unsupported("non-table UPDATE target".into())),
1593 };
1594
1595 let assignments = update
1596 .assignments
1597 .iter()
1598 .map(|a| {
1599 let col = match &a.target {
1600 sp::AssignmentTarget::ColumnName(name) => object_name_to_string(name),
1601 _ => return Err(SqlError::Unsupported("tuple assignment".into())),
1602 };
1603 let expr = convert_expr(&a.value)?;
1604 Ok((col, expr))
1605 })
1606 .collect::<Result<_>>()?;
1607
1608 let where_clause = update.selection.as_ref().map(convert_expr).transpose()?;
1609 let returning = convert_returning(update.returning.as_deref())?;
1610
1611 Ok(Statement::Update(UpdateStmt {
1612 table,
1613 assignments,
1614 where_clause,
1615 returning,
1616 }))
1617}
1618
1619fn convert_delete(delete: sp::Delete) -> Result<Statement> {
1620 let table_name = match &delete.from {
1621 sp::FromTable::WithFromKeyword(tables) => {
1622 if tables.len() != 1 {
1623 return Err(SqlError::Unsupported("multi-table DELETE".into()));
1624 }
1625 match &tables[0].relation {
1626 sp::TableFactor::Table { name, .. } => object_name_to_string(name),
1627 _ => return Err(SqlError::Unsupported("non-table DELETE target".into())),
1628 }
1629 }
1630 sp::FromTable::WithoutKeyword(tables) => {
1631 if tables.len() != 1 {
1632 return Err(SqlError::Unsupported("multi-table DELETE".into()));
1633 }
1634 match &tables[0].relation {
1635 sp::TableFactor::Table { name, .. } => object_name_to_string(name),
1636 _ => return Err(SqlError::Unsupported("non-table DELETE target".into())),
1637 }
1638 }
1639 };
1640
1641 let where_clause = delete.selection.as_ref().map(convert_expr).transpose()?;
1642 let returning = convert_returning(delete.returning.as_deref())?;
1643
1644 Ok(Statement::Delete(DeleteStmt {
1645 table: table_name,
1646 where_clause,
1647 returning,
1648 }))
1649}
1650
1651fn convert_expr(expr: &sp::Expr) -> Result<Expr> {
1652 match expr {
1653 sp::Expr::Value(v) => convert_value(&v.value),
1654 sp::Expr::Identifier(ident) => Ok(Expr::Column(ident.value.to_ascii_lowercase())),
1655 sp::Expr::CompoundIdentifier(parts) => {
1656 if parts.len() == 2 {
1657 Ok(Expr::QualifiedColumn {
1658 table: parts[0].value.to_ascii_lowercase(),
1659 column: parts[1].value.to_ascii_lowercase(),
1660 })
1661 } else {
1662 Ok(Expr::Column(
1663 parts.last().unwrap().value.to_ascii_lowercase(),
1664 ))
1665 }
1666 }
1667 sp::Expr::BinaryOp { left, op, right } => {
1668 let bin_op = convert_bin_op(op)?;
1669 Ok(Expr::BinaryOp {
1670 left: Box::new(convert_expr(left)?),
1671 op: bin_op,
1672 right: Box::new(convert_expr(right)?),
1673 })
1674 }
1675 sp::Expr::UnaryOp { op, expr } => {
1676 let unary_op = match op {
1677 sp::UnaryOperator::Minus => UnaryOp::Neg,
1678 sp::UnaryOperator::Not => UnaryOp::Not,
1679 _ => return Err(SqlError::Unsupported(format!("unary op: {op}"))),
1680 };
1681 Ok(Expr::UnaryOp {
1682 op: unary_op,
1683 expr: Box::new(convert_expr(expr)?),
1684 })
1685 }
1686 sp::Expr::IsNull(e) => Ok(Expr::IsNull(Box::new(convert_expr(e)?))),
1687 sp::Expr::IsNotNull(e) => Ok(Expr::IsNotNull(Box::new(convert_expr(e)?))),
1688 sp::Expr::Nested(e) => convert_expr(e),
1689 sp::Expr::Function(func) => convert_function(func),
1690 sp::Expr::InSubquery {
1691 expr: e,
1692 subquery,
1693 negated,
1694 } => {
1695 let inner_expr = convert_expr(e)?;
1696 let stmt = convert_subquery(subquery)?;
1697 Ok(Expr::InSubquery {
1698 expr: Box::new(inner_expr),
1699 subquery: Box::new(stmt),
1700 negated: *negated,
1701 })
1702 }
1703 sp::Expr::InList {
1704 expr: e,
1705 list,
1706 negated,
1707 } => {
1708 let inner_expr = convert_expr(e)?;
1709 let items = list.iter().map(convert_expr).collect::<Result<Vec<_>>>()?;
1710 Ok(Expr::InList {
1711 expr: Box::new(inner_expr),
1712 list: items,
1713 negated: *negated,
1714 })
1715 }
1716 sp::Expr::Exists { subquery, negated } => {
1717 let stmt = convert_subquery(subquery)?;
1718 Ok(Expr::Exists {
1719 subquery: Box::new(stmt),
1720 negated: *negated,
1721 })
1722 }
1723 sp::Expr::Subquery(query) => {
1724 let stmt = convert_subquery(query)?;
1725 Ok(Expr::ScalarSubquery(Box::new(stmt)))
1726 }
1727 sp::Expr::Between {
1728 expr: e,
1729 negated,
1730 low,
1731 high,
1732 } => Ok(Expr::Between {
1733 expr: Box::new(convert_expr(e)?),
1734 low: Box::new(convert_expr(low)?),
1735 high: Box::new(convert_expr(high)?),
1736 negated: *negated,
1737 }),
1738 sp::Expr::Like {
1739 expr: e,
1740 negated,
1741 pattern,
1742 escape_char,
1743 ..
1744 } => {
1745 let esc = escape_char
1746 .as_ref()
1747 .map(convert_escape_value)
1748 .transpose()?
1749 .map(Box::new);
1750 Ok(Expr::Like {
1751 expr: Box::new(convert_expr(e)?),
1752 pattern: Box::new(convert_expr(pattern)?),
1753 escape: esc,
1754 negated: *negated,
1755 })
1756 }
1757 sp::Expr::ILike {
1758 expr: e,
1759 negated,
1760 pattern,
1761 escape_char,
1762 ..
1763 } => {
1764 let esc = escape_char
1765 .as_ref()
1766 .map(convert_escape_value)
1767 .transpose()?
1768 .map(Box::new);
1769 Ok(Expr::Like {
1770 expr: Box::new(convert_expr(e)?),
1771 pattern: Box::new(convert_expr(pattern)?),
1772 escape: esc,
1773 negated: *negated,
1774 })
1775 }
1776 sp::Expr::Case {
1777 operand,
1778 conditions,
1779 else_result,
1780 ..
1781 } => {
1782 let op = operand
1783 .as_ref()
1784 .map(|e| convert_expr(e))
1785 .transpose()?
1786 .map(Box::new);
1787 let conds: Vec<(Expr, Expr)> = conditions
1788 .iter()
1789 .map(|cw| Ok((convert_expr(&cw.condition)?, convert_expr(&cw.result)?)))
1790 .collect::<Result<_>>()?;
1791 let else_r = else_result
1792 .as_ref()
1793 .map(|e| convert_expr(e))
1794 .transpose()?
1795 .map(Box::new);
1796 Ok(Expr::Case {
1797 operand: op,
1798 conditions: conds,
1799 else_result: else_r,
1800 })
1801 }
1802 sp::Expr::Cast {
1803 expr: e,
1804 data_type: dt,
1805 ..
1806 } => {
1807 let target = convert_data_type(dt)?;
1808 Ok(Expr::Cast {
1809 expr: Box::new(convert_expr(e)?),
1810 data_type: target,
1811 })
1812 }
1813 sp::Expr::Substring {
1814 expr: e,
1815 substring_from,
1816 substring_for,
1817 ..
1818 } => {
1819 let mut args = vec![convert_expr(e)?];
1820 if let Some(from) = substring_from {
1821 args.push(convert_expr(from)?);
1822 }
1823 if let Some(f) = substring_for {
1824 args.push(convert_expr(f)?);
1825 }
1826 Ok(Expr::Function {
1827 name: "SUBSTR".into(),
1828 args,
1829 distinct: false,
1830 })
1831 }
1832 sp::Expr::Trim {
1833 expr: e,
1834 trim_where,
1835 trim_what,
1836 trim_characters,
1837 } => {
1838 let fn_name = match trim_where {
1839 Some(sp::TrimWhereField::Leading) => "LTRIM",
1840 Some(sp::TrimWhereField::Trailing) => "RTRIM",
1841 _ => "TRIM",
1842 };
1843 let mut args = vec![convert_expr(e)?];
1844 if let Some(what) = trim_what {
1845 args.push(convert_expr(what)?);
1846 } else if let Some(chars) = trim_characters {
1847 if let Some(first) = chars.first() {
1848 args.push(convert_expr(first)?);
1849 }
1850 }
1851 Ok(Expr::Function {
1852 name: fn_name.into(),
1853 args,
1854 distinct: false,
1855 })
1856 }
1857 sp::Expr::Ceil { expr: e, .. } => Ok(Expr::Function {
1858 name: "CEIL".into(),
1859 args: vec![convert_expr(e)?],
1860 distinct: false,
1861 }),
1862 sp::Expr::Floor { expr: e, .. } => Ok(Expr::Function {
1863 name: "FLOOR".into(),
1864 args: vec![convert_expr(e)?],
1865 distinct: false,
1866 }),
1867 sp::Expr::Position { expr: e, r#in } => Ok(Expr::Function {
1868 name: "INSTR".into(),
1869 args: vec![convert_expr(r#in)?, convert_expr(e)?],
1870 distinct: false,
1871 }),
1872 sp::Expr::TypedString(ts) => {
1874 let raw = match &ts.value.value {
1875 sp::Value::SingleQuotedString(s) => s.clone(),
1876 sp::Value::DoubleQuotedString(s) => s.clone(),
1877 other => other.to_string(),
1878 };
1879 convert_typed_string(&ts.data_type, &raw)
1880 }
1881 sp::Expr::Interval(iv) => convert_interval_expr(iv),
1883 sp::Expr::Extract { field, expr: e, .. } => {
1885 let field_name = match field {
1886 sp::DateTimeField::Year => "year",
1887 sp::DateTimeField::Month => "month",
1888 sp::DateTimeField::Week(_) => "week",
1889 sp::DateTimeField::Day => "day",
1890 sp::DateTimeField::Date => "day",
1891 sp::DateTimeField::Hour => "hour",
1892 sp::DateTimeField::Minute => "minute",
1893 sp::DateTimeField::Second => "second",
1894 sp::DateTimeField::Millisecond => "milliseconds",
1895 sp::DateTimeField::Microsecond => "microseconds",
1896 sp::DateTimeField::Microseconds => "microseconds",
1897 sp::DateTimeField::Milliseconds => "milliseconds",
1898 sp::DateTimeField::Dow => "dow",
1899 sp::DateTimeField::Isodow => "isodow",
1900 sp::DateTimeField::Doy => "doy",
1901 sp::DateTimeField::Epoch => "epoch",
1902 sp::DateTimeField::Quarter => "quarter",
1903 sp::DateTimeField::Decade => "decade",
1904 sp::DateTimeField::Century => "century",
1905 sp::DateTimeField::Millennium => "millennium",
1906 sp::DateTimeField::Isoyear => "isoyear",
1907 sp::DateTimeField::Julian => "julian",
1908 other => {
1909 return Err(SqlError::InvalidExtractField(format!("{other:?}")));
1910 }
1911 };
1912 Ok(Expr::Function {
1913 name: "EXTRACT".into(),
1914 args: vec![
1915 Expr::Literal(Value::Text(field_name.into())),
1916 convert_expr(e)?,
1917 ],
1918 distinct: false,
1919 })
1920 }
1921 sp::Expr::AtTimeZone {
1923 timestamp,
1924 time_zone,
1925 } => Ok(Expr::Function {
1926 name: "AT_TIMEZONE".into(),
1927 args: vec![convert_expr(timestamp)?, convert_expr(time_zone)?],
1928 distinct: false,
1929 }),
1930 _ => Err(SqlError::Unsupported(format!("expression: {expr}"))),
1931 }
1932}
1933
1934fn convert_value(val: &sp::Value) -> Result<Expr> {
1935 match val {
1936 sp::Value::Number(n, _) => {
1937 if let Ok(i) = n.parse::<i64>() {
1938 Ok(Expr::Literal(Value::Integer(i)))
1939 } else if let Ok(f) = n.parse::<f64>() {
1940 Ok(Expr::Literal(Value::Real(f)))
1941 } else {
1942 Err(SqlError::InvalidValue(format!("cannot parse number: {n}")))
1943 }
1944 }
1945 sp::Value::SingleQuotedString(s) => Ok(Expr::Literal(Value::Text(s.as_str().into()))),
1946 sp::Value::Boolean(b) => Ok(Expr::Literal(Value::Boolean(*b))),
1947 sp::Value::Null => Ok(Expr::Literal(Value::Null)),
1948 sp::Value::Placeholder(s) => {
1949 let idx_str = s
1950 .strip_prefix('$')
1951 .ok_or_else(|| SqlError::Parse(format!("invalid placeholder: {s}")))?;
1952 let idx: usize = idx_str
1953 .parse()
1954 .map_err(|_| SqlError::Parse(format!("invalid placeholder index: {s}")))?;
1955 if idx == 0 {
1956 return Err(SqlError::Parse("placeholder index must be >= 1".into()));
1957 }
1958 Ok(Expr::Parameter(idx))
1959 }
1960 _ => Err(SqlError::Unsupported(format!("value type: {val}"))),
1961 }
1962}
1963
1964fn convert_typed_string(dt: &sp::DataType, value: &str) -> Result<Expr> {
1965 let s = value.trim_matches('\'');
1966 match dt {
1967 sp::DataType::Date => {
1968 let d = crate::datetime::parse_date(s)?;
1969 Ok(Expr::Literal(Value::Date(d)))
1970 }
1971 sp::DataType::Time(_, _) => {
1972 let t = crate::datetime::parse_time(s)?;
1973 Ok(Expr::Literal(Value::Time(t)))
1974 }
1975 sp::DataType::Timestamp(_, _) => {
1976 let t = crate::datetime::parse_timestamp(s)?;
1977 Ok(Expr::Literal(Value::Timestamp(t)))
1978 }
1979 sp::DataType::Interval { .. } => {
1980 let (months, days, micros) = crate::datetime::parse_interval(s)?;
1981 Ok(Expr::Literal(Value::Interval {
1982 months,
1983 days,
1984 micros,
1985 }))
1986 }
1987 _ => {
1988 let target = convert_data_type(dt)?;
1989 Ok(Expr::Cast {
1990 expr: Box::new(Expr::Literal(Value::Text(s.into()))),
1991 data_type: target,
1992 })
1993 }
1994 }
1995}
1996
1997fn convert_interval_expr(iv: &sp::Interval) -> Result<Expr> {
1998 let raw = match iv.value.as_ref() {
1999 sp::Expr::Value(v) => match &v.value {
2000 sp::Value::SingleQuotedString(s) => s.clone(),
2001 sp::Value::Number(n, _) => n.clone(),
2002 other => {
2003 return Err(SqlError::InvalidIntervalLiteral(format!(
2004 "unsupported inner value: {other}"
2005 )))
2006 }
2007 },
2008 other => {
2009 return Err(SqlError::InvalidIntervalLiteral(format!(
2010 "unsupported inner expr: {other}"
2011 )))
2012 }
2013 };
2014
2015 let with_unit = if let Some(field) = &iv.leading_field {
2017 let unit_name = match field {
2018 sp::DateTimeField::Year => "years",
2019 sp::DateTimeField::Month => "months",
2020 sp::DateTimeField::Week(_) => "weeks",
2021 sp::DateTimeField::Day => "days",
2022 sp::DateTimeField::Hour => "hours",
2023 sp::DateTimeField::Minute => "minutes",
2024 sp::DateTimeField::Second => "seconds",
2025 _ => {
2026 return Err(SqlError::InvalidIntervalLiteral(format!(
2027 "unsupported leading field: {field:?}"
2028 )))
2029 }
2030 };
2031 format!("{raw} {unit_name}")
2032 } else {
2033 raw
2034 };
2035
2036 let (months, days, micros) = crate::datetime::parse_interval(&with_unit)?;
2037 Ok(Expr::Literal(Value::Interval {
2038 months,
2039 days,
2040 micros,
2041 }))
2042}
2043
2044fn convert_escape_value(val: &sp::Value) -> Result<Expr> {
2045 match val {
2046 sp::Value::SingleQuotedString(s) => Ok(Expr::Literal(Value::Text(s.as_str().into()))),
2047 _ => Err(SqlError::Unsupported(format!("ESCAPE value: {val}"))),
2048 }
2049}
2050
2051fn convert_bin_op(op: &sp::BinaryOperator) -> Result<BinOp> {
2052 match op {
2053 sp::BinaryOperator::Plus => Ok(BinOp::Add),
2054 sp::BinaryOperator::Minus => Ok(BinOp::Sub),
2055 sp::BinaryOperator::Multiply => Ok(BinOp::Mul),
2056 sp::BinaryOperator::Divide => Ok(BinOp::Div),
2057 sp::BinaryOperator::Modulo => Ok(BinOp::Mod),
2058 sp::BinaryOperator::Eq => Ok(BinOp::Eq),
2059 sp::BinaryOperator::NotEq => Ok(BinOp::NotEq),
2060 sp::BinaryOperator::Lt => Ok(BinOp::Lt),
2061 sp::BinaryOperator::Gt => Ok(BinOp::Gt),
2062 sp::BinaryOperator::LtEq => Ok(BinOp::LtEq),
2063 sp::BinaryOperator::GtEq => Ok(BinOp::GtEq),
2064 sp::BinaryOperator::And => Ok(BinOp::And),
2065 sp::BinaryOperator::Or => Ok(BinOp::Or),
2066 sp::BinaryOperator::StringConcat => Ok(BinOp::Concat),
2067 _ => Err(SqlError::Unsupported(format!("binary op: {op}"))),
2068 }
2069}
2070
2071fn convert_function(func: &sp::Function) -> Result<Expr> {
2072 let name = object_name_to_string(&func.name).to_ascii_uppercase();
2073
2074 let (args, is_count_star, distinct) = match &func.args {
2075 sp::FunctionArguments::List(list) => {
2076 let distinct = matches!(
2077 list.duplicate_treatment,
2078 Some(sp::DuplicateTreatment::Distinct)
2079 );
2080 if list.args.is_empty() && name == "COUNT" {
2081 (vec![], true, distinct)
2082 } else {
2083 let mut count_star = false;
2084 let args = list
2085 .args
2086 .iter()
2087 .map(|arg| match arg {
2088 sp::FunctionArg::Unnamed(sp::FunctionArgExpr::Expr(e)) => convert_expr(e),
2089 sp::FunctionArg::Unnamed(sp::FunctionArgExpr::Wildcard) => {
2090 if name == "COUNT" {
2091 count_star = true;
2092 Ok(Expr::CountStar)
2093 } else {
2094 Err(SqlError::Unsupported(format!("{name}(*)")))
2095 }
2096 }
2097 _ => Err(SqlError::Unsupported(format!(
2098 "function arg type in {name}"
2099 ))),
2100 })
2101 .collect::<Result<Vec<_>>>()?;
2102 if name == "COUNT" && args.len() == 1 && count_star {
2103 (vec![], true, distinct)
2104 } else {
2105 (args, false, distinct)
2106 }
2107 }
2108 }
2109 sp::FunctionArguments::None => {
2110 if name == "COUNT" {
2111 (vec![], true, false)
2112 } else {
2113 (vec![], false, false)
2114 }
2115 }
2116 sp::FunctionArguments::Subquery(_) => {
2117 return Err(SqlError::Unsupported("subquery in function".into()));
2118 }
2119 };
2120
2121 if let Some(over) = &func.over {
2123 let spec = match over {
2124 sp::WindowType::WindowSpec(ws) => convert_window_spec(ws)?,
2125 sp::WindowType::NamedWindow(_) => {
2126 return Err(SqlError::Unsupported("named windows".into()));
2127 }
2128 };
2129 return Ok(Expr::WindowFunction { name, args, spec });
2130 }
2131
2132 if is_count_star {
2134 return Ok(Expr::CountStar);
2135 }
2136
2137 if name == "COALESCE" {
2138 if args.is_empty() {
2139 return Err(SqlError::Parse(
2140 "COALESCE requires at least one argument".into(),
2141 ));
2142 }
2143 return Ok(Expr::Coalesce(args));
2144 }
2145
2146 if name == "NULLIF" {
2147 if args.len() != 2 {
2148 return Err(SqlError::Parse(
2149 "NULLIF requires exactly two arguments".into(),
2150 ));
2151 }
2152 return Ok(Expr::Case {
2153 operand: None,
2154 conditions: vec![(
2155 Expr::BinaryOp {
2156 left: Box::new(args[0].clone()),
2157 op: BinOp::Eq,
2158 right: Box::new(args[1].clone()),
2159 },
2160 Expr::Literal(Value::Null),
2161 )],
2162 else_result: Some(Box::new(args[0].clone())),
2163 });
2164 }
2165
2166 if name == "IIF" {
2167 if args.len() != 3 {
2168 return Err(SqlError::Parse(
2169 "IIF requires exactly three arguments".into(),
2170 ));
2171 }
2172 return Ok(Expr::Case {
2173 operand: None,
2174 conditions: vec![(args[0].clone(), args[1].clone())],
2175 else_result: Some(Box::new(args[2].clone())),
2176 });
2177 }
2178
2179 Ok(Expr::Function {
2180 name,
2181 args,
2182 distinct,
2183 })
2184}
2185
2186fn convert_window_spec(ws: &sp::WindowSpec) -> Result<WindowSpec> {
2187 let partition_by = ws
2188 .partition_by
2189 .iter()
2190 .map(convert_expr)
2191 .collect::<Result<Vec<_>>>()?;
2192 let order_by = ws
2193 .order_by
2194 .iter()
2195 .map(convert_order_by_expr)
2196 .collect::<Result<Vec<_>>>()?;
2197 let frame = ws
2198 .window_frame
2199 .as_ref()
2200 .map(convert_window_frame)
2201 .transpose()?;
2202 Ok(WindowSpec {
2203 partition_by,
2204 order_by,
2205 frame,
2206 })
2207}
2208
2209fn convert_window_frame(wf: &sp::WindowFrame) -> Result<WindowFrame> {
2210 let units = match wf.units {
2211 sp::WindowFrameUnits::Rows => WindowFrameUnits::Rows,
2212 sp::WindowFrameUnits::Range => WindowFrameUnits::Range,
2213 sp::WindowFrameUnits::Groups => {
2214 return Err(SqlError::Unsupported("GROUPS window frame".into()));
2215 }
2216 };
2217 let start = convert_window_frame_bound(&wf.start_bound)?;
2218 let end = match &wf.end_bound {
2219 Some(b) => convert_window_frame_bound(b)?,
2220 None => WindowFrameBound::CurrentRow,
2221 };
2222 Ok(WindowFrame { units, start, end })
2223}
2224
2225fn convert_window_frame_bound(b: &sp::WindowFrameBound) -> Result<WindowFrameBound> {
2226 match b {
2227 sp::WindowFrameBound::CurrentRow => Ok(WindowFrameBound::CurrentRow),
2228 sp::WindowFrameBound::Preceding(None) => Ok(WindowFrameBound::UnboundedPreceding),
2229 sp::WindowFrameBound::Preceding(Some(e)) => {
2230 Ok(WindowFrameBound::Preceding(Box::new(convert_expr(e)?)))
2231 }
2232 sp::WindowFrameBound::Following(None) => Ok(WindowFrameBound::UnboundedFollowing),
2233 sp::WindowFrameBound::Following(Some(e)) => {
2234 Ok(WindowFrameBound::Following(Box::new(convert_expr(e)?)))
2235 }
2236 }
2237}
2238
2239fn convert_returning(items: Option<&[sp::SelectItem]>) -> Result<Option<Vec<SelectColumn>>> {
2240 match items {
2241 None => Ok(None),
2242 Some(items) => {
2243 let cols = items
2244 .iter()
2245 .map(convert_returning_item)
2246 .collect::<Result<Vec<_>>>()?;
2247 Ok(Some(cols))
2248 }
2249 }
2250}
2251
2252fn convert_returning_item(item: &sp::SelectItem) -> Result<SelectColumn> {
2253 match item {
2254 sp::SelectItem::Wildcard(_) => Ok(SelectColumn::AllColumns),
2255 sp::SelectItem::UnnamedExpr(e) => {
2256 reject_aggregate_or_window(e, "RETURNING")?;
2257 Ok(SelectColumn::Expr {
2258 expr: convert_expr(e)?,
2259 alias: None,
2260 })
2261 }
2262 sp::SelectItem::ExprWithAlias { expr, alias } => {
2263 reject_aggregate_or_window(expr, "RETURNING")?;
2264 Ok(SelectColumn::Expr {
2265 expr: convert_expr(expr)?,
2266 alias: Some(alias.value.clone()),
2267 })
2268 }
2269 sp::SelectItem::QualifiedWildcard(kind, _) => match kind {
2270 sp::SelectItemQualifiedWildcardKind::ObjectName(name) => {
2271 let s = object_name_to_string(name);
2272 if s.eq_ignore_ascii_case("old") {
2273 Ok(SelectColumn::AllFromOld)
2274 } else if s.eq_ignore_ascii_case("new") {
2275 Ok(SelectColumn::AllFromNew)
2276 } else {
2277 Err(SqlError::Unsupported(format!(
2278 "RETURNING {s}.* — only old.* and new.* qualified wildcards allowed"
2279 )))
2280 }
2281 }
2282 sp::SelectItemQualifiedWildcardKind::Expr(_) => {
2283 Err(SqlError::Unsupported("expression.* in RETURNING".into()))
2284 }
2285 },
2286 }
2287}
2288
2289fn reject_aggregate_or_window(expr: &sp::Expr, ctx: &str) -> Result<()> {
2290 use sp::Expr as E;
2291 match expr {
2292 E::Function(f) => {
2293 if f.over.is_some() {
2294 return Err(SqlError::Unsupported(format!(
2295 "window functions are not allowed in {ctx}"
2296 )));
2297 }
2298 let name = f
2299 .name
2300 .0
2301 .last()
2302 .map(|p| match p {
2303 sp::ObjectNamePart::Identifier(id) => id.value.to_ascii_uppercase(),
2304 _ => String::new(),
2305 })
2306 .unwrap_or_default();
2307 if matches!(
2308 name.as_str(),
2309 "COUNT"
2310 | "SUM"
2311 | "AVG"
2312 | "MIN"
2313 | "MAX"
2314 | "GROUP_CONCAT"
2315 | "STRING_AGG"
2316 | "ARRAY_AGG"
2317 | "BIT_AND"
2318 | "BIT_OR"
2319 | "BOOL_AND"
2320 | "BOOL_OR"
2321 | "EVERY"
2322 | "STDDEV"
2323 | "STDDEV_POP"
2324 | "STDDEV_SAMP"
2325 | "VARIANCE"
2326 | "VAR_POP"
2327 | "VAR_SAMP"
2328 ) {
2329 return Err(SqlError::Unsupported(format!(
2330 "aggregate functions are not allowed in {ctx}"
2331 )));
2332 }
2333 for arg in walk_function_args(f) {
2334 reject_aggregate_or_window(arg, ctx)?;
2335 }
2336 Ok(())
2337 }
2338 E::BinaryOp { left, right, .. } => {
2339 reject_aggregate_or_window(left, ctx)?;
2340 reject_aggregate_or_window(right, ctx)
2341 }
2342 E::UnaryOp { expr, .. } => reject_aggregate_or_window(expr, ctx),
2343 E::Cast { expr, .. } => reject_aggregate_or_window(expr, ctx),
2344 E::Nested(e) => reject_aggregate_or_window(e, ctx),
2345 E::Case {
2346 conditions,
2347 else_result,
2348 ..
2349 } => {
2350 for cwt in conditions {
2351 reject_aggregate_or_window(&cwt.condition, ctx)?;
2352 reject_aggregate_or_window(&cwt.result, ctx)?;
2353 }
2354 if let Some(e) = else_result {
2355 reject_aggregate_or_window(e, ctx)?;
2356 }
2357 Ok(())
2358 }
2359 _ => Ok(()),
2360 }
2361}
2362
2363fn walk_function_args(f: &sp::Function) -> Vec<&sp::Expr> {
2364 use sp::FunctionArguments as FA;
2365 let mut out = Vec::new();
2366 if let FA::List(args) = &f.args {
2367 for a in &args.args {
2368 if let sp::FunctionArg::Unnamed(sp::FunctionArgExpr::Expr(e)) = a {
2369 out.push(e);
2370 }
2371 }
2372 }
2373 out
2374}
2375
2376fn convert_select_item(item: &sp::SelectItem) -> Result<SelectColumn> {
2377 match item {
2378 sp::SelectItem::Wildcard(_) => Ok(SelectColumn::AllColumns),
2379 sp::SelectItem::UnnamedExpr(e) => {
2380 let expr = convert_expr(e)?;
2381 Ok(SelectColumn::Expr { expr, alias: None })
2382 }
2383 sp::SelectItem::ExprWithAlias { expr, alias } => {
2384 let expr = convert_expr(expr)?;
2385 Ok(SelectColumn::Expr {
2386 expr,
2387 alias: Some(alias.value.clone()),
2388 })
2389 }
2390 sp::SelectItem::QualifiedWildcard(_, _) => {
2391 Err(SqlError::Unsupported("qualified wildcard (table.*)".into()))
2392 }
2393 }
2394}
2395
2396fn convert_order_by_expr(expr: &sp::OrderByExpr) -> Result<OrderByItem> {
2397 let e = convert_expr(&expr.expr)?;
2398 let descending = expr.options.asc.map(|asc| !asc).unwrap_or(false);
2399 let nulls_first = expr.options.nulls_first;
2400
2401 Ok(OrderByItem {
2402 expr: e,
2403 descending,
2404 nulls_first,
2405 })
2406}
2407
2408fn convert_data_type(dt: &sp::DataType) -> Result<DataType> {
2409 match dt {
2410 sp::DataType::Int(_)
2411 | sp::DataType::Integer(_)
2412 | sp::DataType::BigInt(_)
2413 | sp::DataType::SmallInt(_)
2414 | sp::DataType::TinyInt(_)
2415 | sp::DataType::Int2(_)
2416 | sp::DataType::Int4(_)
2417 | sp::DataType::Int8(_) => Ok(DataType::Integer),
2418
2419 sp::DataType::Real
2420 | sp::DataType::Double(..)
2421 | sp::DataType::DoublePrecision
2422 | sp::DataType::Float(_)
2423 | sp::DataType::Float4
2424 | sp::DataType::Float64 => Ok(DataType::Real),
2425
2426 sp::DataType::Varchar(_)
2427 | sp::DataType::Text
2428 | sp::DataType::Char(_)
2429 | sp::DataType::Character(_)
2430 | sp::DataType::String(_) => Ok(DataType::Text),
2431
2432 sp::DataType::Blob(_) | sp::DataType::Bytea => Ok(DataType::Blob),
2433
2434 sp::DataType::Boolean | sp::DataType::Bool => Ok(DataType::Boolean),
2435
2436 sp::DataType::Date => Ok(DataType::Date),
2437 sp::DataType::Time(_, _) => Ok(DataType::Time),
2438 sp::DataType::Timestamp(_, _) => Ok(DataType::Timestamp),
2439 sp::DataType::Interval { .. } => Ok(DataType::Interval),
2440
2441 _ => Err(SqlError::Unsupported(format!("data type: {dt}"))),
2442 }
2443}
2444
2445fn object_name_to_string(name: &sp::ObjectName) -> String {
2446 name.0
2447 .iter()
2448 .filter_map(|p| match p {
2449 sp::ObjectNamePart::Identifier(ident) => Some(ident.value.clone()),
2450 _ => None,
2451 })
2452 .collect::<Vec<_>>()
2453 .join(".")
2454}
2455
2456#[cfg(test)]
2457#[path = "parser_tests.rs"]
2458mod tests;