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