1use std::collections::HashMap;
6
7#[allow(dead_code)]
8#[derive(Debug, Clone, PartialEq)]
9pub enum SQLJoinType {
10 Inner,
11 Left,
12 Right,
13 Full,
14 Cross,
15 Natural,
16}
17impl SQLJoinType {
18 #[allow(dead_code)]
19 pub fn keyword(&self) -> &str {
20 match self {
21 SQLJoinType::Inner => "INNER JOIN",
22 SQLJoinType::Left => "LEFT JOIN",
23 SQLJoinType::Right => "RIGHT JOIN",
24 SQLJoinType::Full => "FULL OUTER JOIN",
25 SQLJoinType::Cross => "CROSS JOIN",
26 SQLJoinType::Natural => "NATURAL JOIN",
27 }
28 }
29}
30#[allow(dead_code)]
31pub struct SQLPreparedStatement {
32 pub sql: String,
33 pub parameters: Vec<SQLParameter>,
34 pub dialect: SQLDialect,
35}
36impl SQLPreparedStatement {
37 #[allow(dead_code)]
38 pub fn new(sql: impl Into<String>, dialect: SQLDialect) -> Self {
39 SQLPreparedStatement {
40 sql: sql.into(),
41 parameters: Vec::new(),
42 dialect,
43 }
44 }
45 #[allow(dead_code)]
46 pub fn add_param(&mut self, name: impl Into<String>, ty: SQLType) {
47 let index = self.parameters.len() + 1;
48 self.parameters.push(SQLParameter {
49 name: name.into(),
50 ty,
51 index,
52 });
53 }
54 #[allow(dead_code)]
55 pub fn placeholder(&self, index: usize) -> String {
56 match self.dialect {
57 SQLDialect::PostgreSQL => format!("${}", index),
58 SQLDialect::MySQL | SQLDialect::SQLite => "?".to_string(),
59 SQLDialect::MSSQL => format!("@p{}", index),
60 }
61 }
62}
63#[allow(dead_code)]
64pub struct SQLSchemaInspector {
65 pub dialect: SQLDialect,
66}
67impl SQLSchemaInspector {
68 #[allow(dead_code)]
69 pub fn new(dialect: SQLDialect) -> Self {
70 SQLSchemaInspector { dialect }
71 }
72 #[allow(dead_code)]
73 pub fn list_tables_query(&self) -> &str {
74 match self.dialect {
75 SQLDialect::SQLite => "SELECT name FROM sqlite_master WHERE type='table'",
76 SQLDialect::PostgreSQL => "SELECT tablename FROM pg_tables WHERE schemaname='public'",
77 SQLDialect::MySQL => "SHOW TABLES",
78 SQLDialect::MSSQL => {
79 "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'"
80 }
81 }
82 }
83 #[allow(dead_code)]
84 pub fn column_info_query(&self, table: &str) -> String {
85 match self.dialect {
86 SQLDialect::SQLite => format!("PRAGMA table_info({})", table),
87 SQLDialect::PostgreSQL => {
88 format!(
89 "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = '{}'",
90 table
91 )
92 }
93 _ => format!("DESCRIBE {}", table),
94 }
95 }
96 #[allow(dead_code)]
97 pub fn index_info_query(&self, table: &str) -> String {
98 match self.dialect {
99 SQLDialect::SQLite => format!("PRAGMA index_list({})", table),
100 SQLDialect::PostgreSQL => {
101 format!(
102 "SELECT indexname, indexdef FROM pg_indexes WHERE tablename = '{}'",
103 table
104 )
105 }
106 SQLDialect::MySQL => format!("SHOW INDEX FROM {}", table),
107 _ => {
108 format!(
109 "SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID('{}')",
110 table
111 )
112 }
113 }
114 }
115}
116#[allow(dead_code)]
117#[derive(Debug, Clone)]
118pub enum SQLParamDirection {
119 In,
120 Out,
121 InOut,
122 Variadic,
123}
124impl SQLParamDirection {
125 #[allow(dead_code)]
126 pub fn keyword(&self) -> &str {
127 match self {
128 SQLParamDirection::In => "IN",
129 SQLParamDirection::Out => "OUT",
130 SQLParamDirection::InOut => "INOUT",
131 SQLParamDirection::Variadic => "VARIADIC",
132 }
133 }
134}
135#[allow(dead_code)]
136#[derive(Debug, Clone)]
137pub enum SQLTriggerEvent {
138 Insert,
139 Update(Vec<String>),
140 Delete,
141 Truncate,
142}
143impl SQLTriggerEvent {
144 #[allow(dead_code)]
145 pub fn keyword(&self) -> String {
146 match self {
147 SQLTriggerEvent::Insert => "INSERT".to_string(),
148 SQLTriggerEvent::Update(cols) => {
149 if cols.is_empty() {
150 "UPDATE".to_string()
151 } else {
152 format!("UPDATE OF {}", cols.join(", "))
153 }
154 }
155 SQLTriggerEvent::Delete => "DELETE".to_string(),
156 SQLTriggerEvent::Truncate => "TRUNCATE".to_string(),
157 }
158 }
159}
160#[allow(dead_code)]
161#[derive(Debug, Clone)]
162pub struct SQLUpdateBuilder {
163 pub table: String,
164 pub sets: Vec<(String, String)>,
165 pub where_clause: Option<String>,
166 pub returning: Vec<String>,
167}
168impl SQLUpdateBuilder {
169 #[allow(dead_code)]
170 pub fn new(table: impl Into<String>) -> Self {
171 SQLUpdateBuilder {
172 table: table.into(),
173 sets: Vec::new(),
174 where_clause: None,
175 returning: Vec::new(),
176 }
177 }
178 #[allow(dead_code)]
179 pub fn set(mut self, col: impl Into<String>, val: impl Into<String>) -> Self {
180 self.sets.push((col.into(), val.into()));
181 self
182 }
183 #[allow(dead_code)]
184 pub fn where_cond(mut self, cond: impl Into<String>) -> Self {
185 self.where_clause = Some(cond.into());
186 self
187 }
188 #[allow(dead_code)]
189 pub fn returning(mut self, col: impl Into<String>) -> Self {
190 self.returning.push(col.into());
191 self
192 }
193 #[allow(dead_code)]
194 pub fn build(&self) -> String {
195 let mut out = format!("UPDATE {} SET ", self.table);
196 let parts: Vec<String> = self
197 .sets
198 .iter()
199 .map(|(k, v)| format!("{} = {}", k, v))
200 .collect();
201 out.push_str(&parts.join(", "));
202 if let Some(ref w) = self.where_clause {
203 out.push_str(&format!(" WHERE {}", w));
204 }
205 if !self.returning.is_empty() {
206 out.push_str(&format!(" RETURNING {}", self.returning.join(", ")));
207 }
208 out
209 }
210}
211#[derive(Debug, Clone, PartialEq, Eq)]
213pub enum SQLDialect {
214 SQLite,
215 PostgreSQL,
216 MySQL,
217 MSSQL,
218}
219#[allow(dead_code)]
220#[derive(Debug, Clone)]
221pub enum SQLWindowFrame {
222 Rows(SQLFrameBound, SQLFrameBound),
223 Range(SQLFrameBound, SQLFrameBound),
224}
225#[allow(dead_code)]
226pub struct SQLTransactionBuilder {
227 pub statements: Vec<String>,
228 pub isolation_level: Option<SQLIsolationLevel>,
229}
230impl SQLTransactionBuilder {
231 #[allow(dead_code)]
232 pub fn new() -> Self {
233 SQLTransactionBuilder {
234 statements: Vec::new(),
235 isolation_level: None,
236 }
237 }
238 #[allow(dead_code)]
239 pub fn isolation(mut self, level: SQLIsolationLevel) -> Self {
240 self.isolation_level = Some(level);
241 self
242 }
243 #[allow(dead_code)]
244 pub fn add_statement(mut self, stmt: impl Into<String>) -> Self {
245 self.statements.push(stmt.into());
246 self
247 }
248 #[allow(dead_code)]
249 pub fn build(&self) -> String {
250 let mut out = String::new();
251 if let Some(ref level) = self.isolation_level {
252 out.push_str(&format!(
253 "SET TRANSACTION ISOLATION LEVEL {};\n",
254 level.keyword()
255 ));
256 }
257 out.push_str("BEGIN;\n");
258 for stmt in &self.statements {
259 out.push_str(stmt);
260 out.push_str(";\n");
261 }
262 out.push_str("COMMIT;");
263 out
264 }
265}
266#[allow(dead_code)]
267#[derive(Debug, Clone)]
268pub struct SQLTableInfo {
269 pub name: String,
270 pub schema: Option<String>,
271 pub columns: Vec<SQLColumnInfo>,
272 pub indexes: Vec<SQLIndexInfo>,
273 pub row_count_estimate: Option<u64>,
274}
275impl SQLTableInfo {
276 #[allow(dead_code)]
277 pub fn new(name: impl Into<String>) -> Self {
278 SQLTableInfo {
279 name: name.into(),
280 schema: None,
281 columns: Vec::new(),
282 indexes: Vec::new(),
283 row_count_estimate: None,
284 }
285 }
286 #[allow(dead_code)]
287 pub fn primary_key_columns(&self) -> Vec<&SQLColumnInfo> {
288 self.columns.iter().filter(|c| c.is_primary_key).collect()
289 }
290 #[allow(dead_code)]
291 pub fn nullable_columns(&self) -> Vec<&SQLColumnInfo> {
292 self.columns.iter().filter(|c| c.is_nullable).collect()
293 }
294 #[allow(dead_code)]
295 pub fn emit_describe_query(&self) -> String {
296 format!(
297 "SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = '{}'",
298 self.name
299 )
300 }
301}
302#[allow(dead_code)]
303#[derive(Debug, Clone)]
304pub enum SQLAlterOperation {
305 AddColumn(SQLColumnDef),
306 DropColumn(String),
307 RenameColumn(String, String),
308 AlterColumnType(String, String),
309 AddConstraint(SQLTableConstraint),
310 DropConstraint(String),
311 RenameTable(String),
312}
313#[allow(dead_code)]
314#[derive(Debug, Clone)]
315pub struct SQLCreateTableBuilder {
316 pub name: String,
317 pub columns: Vec<SQLColumnDef>,
318 pub constraints: Vec<SQLTableConstraint>,
319 pub if_not_exists: bool,
320 pub temporary: bool,
321}
322impl SQLCreateTableBuilder {
323 #[allow(dead_code)]
324 pub fn new(name: impl Into<String>) -> Self {
325 SQLCreateTableBuilder {
326 name: name.into(),
327 columns: Vec::new(),
328 constraints: Vec::new(),
329 if_not_exists: false,
330 temporary: false,
331 }
332 }
333 #[allow(dead_code)]
334 pub fn if_not_exists(mut self) -> Self {
335 self.if_not_exists = true;
336 self
337 }
338 #[allow(dead_code)]
339 pub fn temporary(mut self) -> Self {
340 self.temporary = true;
341 self
342 }
343 #[allow(dead_code)]
344 pub fn column(mut self, col: SQLColumnDef) -> Self {
345 self.columns.push(col);
346 self
347 }
348 #[allow(dead_code)]
349 pub fn constraint(mut self, c: SQLTableConstraint) -> Self {
350 self.constraints.push(c);
351 self
352 }
353 #[allow(dead_code)]
354 pub fn build(&self, dialect: &SQLDialect) -> String {
355 let mut out = String::from("CREATE");
356 if self.temporary {
357 out.push_str(" TEMPORARY");
358 }
359 out.push_str(" TABLE");
360 if self.if_not_exists {
361 out.push_str(" IF NOT EXISTS");
362 }
363 out.push_str(&format!(" {} (\n", self.name));
364 let mut defs: Vec<String> = self
365 .columns
366 .iter()
367 .map(|c| format!(" {}", c.emit(dialect)))
368 .collect();
369 for constraint in &self.constraints {
370 defs.push(format!(" {}", constraint.emit()));
371 }
372 out.push_str(&defs.join(",\n"));
373 out.push_str("\n)");
374 out
375 }
376}
377#[allow(dead_code)]
378pub struct SQLSequenceBuilder {
379 pub name: String,
380 pub start: i64,
381 pub increment: i64,
382 pub min_value: Option<i64>,
383 pub max_value: Option<i64>,
384 pub cycle: bool,
385 pub cache: u64,
386}
387impl SQLSequenceBuilder {
388 #[allow(dead_code)]
389 pub fn new(name: impl Into<String>) -> Self {
390 SQLSequenceBuilder {
391 name: name.into(),
392 start: 1,
393 increment: 1,
394 min_value: None,
395 max_value: None,
396 cycle: false,
397 cache: 1,
398 }
399 }
400 #[allow(dead_code)]
401 pub fn start_with(mut self, n: i64) -> Self {
402 self.start = n;
403 self
404 }
405 #[allow(dead_code)]
406 pub fn increment_by(mut self, n: i64) -> Self {
407 self.increment = n;
408 self
409 }
410 #[allow(dead_code)]
411 pub fn min(mut self, n: i64) -> Self {
412 self.min_value = Some(n);
413 self
414 }
415 #[allow(dead_code)]
416 pub fn max(mut self, n: i64) -> Self {
417 self.max_value = Some(n);
418 self
419 }
420 #[allow(dead_code)]
421 pub fn cycle(mut self) -> Self {
422 self.cycle = true;
423 self
424 }
425 #[allow(dead_code)]
426 pub fn cache(mut self, n: u64) -> Self {
427 self.cache = n;
428 self
429 }
430 #[allow(dead_code)]
431 pub fn build(&self) -> String {
432 let mut out = format!(
433 "CREATE SEQUENCE {} START WITH {} INCREMENT BY {}",
434 self.name, self.start, self.increment
435 );
436 if let Some(min) = self.min_value {
437 out.push_str(&format!(" MINVALUE {}", min));
438 }
439 if let Some(max) = self.max_value {
440 out.push_str(&format!(" MAXVALUE {}", max));
441 }
442 if self.cache > 1 {
443 out.push_str(&format!(" CACHE {}", self.cache));
444 }
445 if self.cycle {
446 out.push_str(" CYCLE");
447 } else {
448 out.push_str(" NO CYCLE");
449 }
450 out
451 }
452}
453#[allow(dead_code)]
454#[derive(Debug, Clone)]
455pub struct SQLColumnDef {
456 pub name: String,
457 pub ty: SQLType,
458 pub nullable: bool,
459 pub primary_key: bool,
460 pub unique: bool,
461 pub default_value: Option<String>,
462 pub references: Option<(String, String)>,
463 pub auto_increment: bool,
464}
465impl SQLColumnDef {
466 #[allow(dead_code)]
467 pub fn new(name: impl Into<String>, ty: SQLType) -> Self {
468 SQLColumnDef {
469 name: name.into(),
470 ty,
471 nullable: true,
472 primary_key: false,
473 unique: false,
474 default_value: None,
475 references: None,
476 auto_increment: false,
477 }
478 }
479 #[allow(dead_code)]
480 pub fn not_null(mut self) -> Self {
481 self.nullable = false;
482 self
483 }
484 #[allow(dead_code)]
485 pub fn primary_key(mut self) -> Self {
486 self.primary_key = true;
487 self.nullable = false;
488 self
489 }
490 #[allow(dead_code)]
491 pub fn unique(mut self) -> Self {
492 self.unique = true;
493 self
494 }
495 #[allow(dead_code)]
496 pub fn default(mut self, val: impl Into<String>) -> Self {
497 self.default_value = Some(val.into());
498 self
499 }
500 #[allow(dead_code)]
501 pub fn auto_increment(mut self) -> Self {
502 self.auto_increment = true;
503 self
504 }
505 #[allow(dead_code)]
506 pub fn references(mut self, table: impl Into<String>, col: impl Into<String>) -> Self {
507 self.references = Some((table.into(), col.into()));
508 self
509 }
510 #[allow(dead_code)]
511 pub fn emit(&self, _dialect: &SQLDialect) -> String {
512 let ty_str = match &self.ty {
513 SQLType::Integer => "INTEGER".to_string(),
514 SQLType::Real => "REAL".to_string(),
515 SQLType::Text => "TEXT".to_string(),
516 SQLType::Boolean => "BOOLEAN".to_string(),
517 SQLType::Blob => "BLOB".to_string(),
518 SQLType::Null => "NULL".to_string(),
519 SQLType::Timestamp => "TIMESTAMP".to_string(),
520 };
521 let mut out = format!("{} {}", self.name, ty_str);
522 if !self.nullable && !self.primary_key {
523 out.push_str(" NOT NULL");
524 }
525 if self.primary_key {
526 out.push_str(" PRIMARY KEY");
527 }
528 if self.auto_increment {
529 out.push_str(" AUTOINCREMENT");
530 }
531 if self.unique {
532 out.push_str(" UNIQUE");
533 }
534 if let Some(ref dv) = self.default_value {
535 out.push_str(&format!(" DEFAULT {}", dv));
536 }
537 if let Some((ref t, ref c)) = self.references {
538 out.push_str(&format!(" REFERENCES {}({})", t, c));
539 }
540 out
541 }
542}
543#[allow(dead_code)]
544#[derive(Debug, Clone)]
545pub struct SQLTrigger {
546 pub name: String,
547 pub table: String,
548 pub timing: SQLTriggerTiming,
549 pub events: Vec<SQLTriggerEvent>,
550 pub function_name: String,
551 pub for_each_row: bool,
552 pub when_condition: Option<String>,
553}
554impl SQLTrigger {
555 #[allow(dead_code)]
556 pub fn new(
557 name: impl Into<String>,
558 table: impl Into<String>,
559 function: impl Into<String>,
560 ) -> Self {
561 SQLTrigger {
562 name: name.into(),
563 table: table.into(),
564 timing: SQLTriggerTiming::After,
565 events: Vec::new(),
566 function_name: function.into(),
567 for_each_row: true,
568 when_condition: None,
569 }
570 }
571 #[allow(dead_code)]
572 pub fn before(mut self) -> Self {
573 self.timing = SQLTriggerTiming::Before;
574 self
575 }
576 #[allow(dead_code)]
577 pub fn after(mut self) -> Self {
578 self.timing = SQLTriggerTiming::After;
579 self
580 }
581 #[allow(dead_code)]
582 pub fn on_insert(mut self) -> Self {
583 self.events.push(SQLTriggerEvent::Insert);
584 self
585 }
586 #[allow(dead_code)]
587 pub fn on_update(mut self) -> Self {
588 self.events.push(SQLTriggerEvent::Update(Vec::new()));
589 self
590 }
591 #[allow(dead_code)]
592 pub fn on_delete(mut self) -> Self {
593 self.events.push(SQLTriggerEvent::Delete);
594 self
595 }
596 #[allow(dead_code)]
597 pub fn for_each_statement(mut self) -> Self {
598 self.for_each_row = false;
599 self
600 }
601 #[allow(dead_code)]
602 pub fn emit(&self) -> String {
603 let timing = match self.timing {
604 SQLTriggerTiming::Before => "BEFORE",
605 SQLTriggerTiming::After => "AFTER",
606 SQLTriggerTiming::InsteadOf => "INSTEAD OF",
607 };
608 let events: Vec<String> = self.events.iter().map(|e| e.keyword()).collect();
609 let row_stmt = if self.for_each_row {
610 "FOR EACH ROW"
611 } else {
612 "FOR EACH STATEMENT"
613 };
614 let mut out = format!(
615 "CREATE OR REPLACE TRIGGER {} {} {} ON {} {}",
616 self.name,
617 timing,
618 events.join(" OR "),
619 self.table,
620 row_stmt
621 );
622 if let Some(ref cond) = self.when_condition {
623 out.push_str(&format!(" WHEN ({})", cond));
624 }
625 out.push_str(&format!(" EXECUTE FUNCTION {}();", self.function_name));
626 out
627 }
628}
629#[allow(dead_code)]
630#[derive(Debug, Clone)]
631pub struct SQLIndexBuilder {
632 pub name: String,
633 pub table: String,
634 pub columns: Vec<String>,
635 pub unique: bool,
636 pub if_not_exists: bool,
637 pub where_clause: Option<String>,
638}
639impl SQLIndexBuilder {
640 #[allow(dead_code)]
641 pub fn new(name: impl Into<String>, table: impl Into<String>) -> Self {
642 SQLIndexBuilder {
643 name: name.into(),
644 table: table.into(),
645 columns: Vec::new(),
646 unique: false,
647 if_not_exists: false,
648 where_clause: None,
649 }
650 }
651 #[allow(dead_code)]
652 pub fn on_column(mut self, col: impl Into<String>) -> Self {
653 self.columns.push(col.into());
654 self
655 }
656 #[allow(dead_code)]
657 pub fn unique(mut self) -> Self {
658 self.unique = true;
659 self
660 }
661 #[allow(dead_code)]
662 pub fn if_not_exists(mut self) -> Self {
663 self.if_not_exists = true;
664 self
665 }
666 #[allow(dead_code)]
667 pub fn where_cond(mut self, cond: impl Into<String>) -> Self {
668 self.where_clause = Some(cond.into());
669 self
670 }
671 #[allow(dead_code)]
672 pub fn build(&self) -> String {
673 let mut out = String::from("CREATE");
674 if self.unique {
675 out.push_str(" UNIQUE");
676 }
677 out.push_str(" INDEX");
678 if self.if_not_exists {
679 out.push_str(" IF NOT EXISTS");
680 }
681 out.push_str(&format!(
682 " {} ON {} ({})",
683 self.name,
684 self.table,
685 self.columns.join(", ")
686 ));
687 if let Some(ref w) = self.where_clause {
688 out.push_str(&format!(" WHERE {}", w));
689 }
690 out
691 }
692}
693#[allow(dead_code)]
694#[derive(Debug, Clone)]
695pub enum SQLTableConstraint {
696 PrimaryKey(Vec<String>),
697 Unique(Vec<String>),
698 ForeignKey {
699 columns: Vec<String>,
700 ref_table: String,
701 ref_columns: Vec<String>,
702 on_delete: Option<SQLForeignKeyAction>,
703 on_update: Option<SQLForeignKeyAction>,
704 },
705 Check(String),
706}
707impl SQLTableConstraint {
708 #[allow(dead_code)]
709 pub fn emit(&self) -> String {
710 match self {
711 SQLTableConstraint::PrimaryKey(cols) => {
712 format!("PRIMARY KEY ({})", cols.join(", "))
713 }
714 SQLTableConstraint::Unique(cols) => format!("UNIQUE ({})", cols.join(", ")),
715 SQLTableConstraint::ForeignKey {
716 columns,
717 ref_table,
718 ref_columns,
719 on_delete,
720 on_update,
721 } => {
722 let mut s = format!(
723 "FOREIGN KEY ({}) REFERENCES {}({})",
724 columns.join(", "),
725 ref_table,
726 ref_columns.join(", ")
727 );
728 if let Some(ref a) = on_delete {
729 s.push_str(&format!(" ON DELETE {}", a.keyword()));
730 }
731 if let Some(ref a) = on_update {
732 s.push_str(&format!(" ON UPDATE {}", a.keyword()));
733 }
734 s
735 }
736 SQLTableConstraint::Check(expr) => format!("CHECK ({})", expr),
737 }
738 }
739}
740#[allow(dead_code)]
741#[derive(Debug, Clone)]
742pub struct SQLMigration {
743 pub version: u32,
744 pub description: String,
745 pub up_statements: Vec<String>,
746 pub down_statements: Vec<String>,
747}
748impl SQLMigration {
749 #[allow(dead_code)]
750 pub fn new(version: u32, description: impl Into<String>) -> Self {
751 SQLMigration {
752 version,
753 description: description.into(),
754 up_statements: Vec::new(),
755 down_statements: Vec::new(),
756 }
757 }
758 #[allow(dead_code)]
759 pub fn up(mut self, stmt: impl Into<String>) -> Self {
760 self.up_statements.push(stmt.into());
761 self
762 }
763 #[allow(dead_code)]
764 pub fn down(mut self, stmt: impl Into<String>) -> Self {
765 self.down_statements.push(stmt.into());
766 self
767 }
768 #[allow(dead_code)]
769 pub fn emit_up(&self) -> String {
770 format!(
771 "-- Migration v{}: {}\n{}",
772 self.version,
773 self.description,
774 self.up_statements.join(";\n")
775 )
776 }
777 #[allow(dead_code)]
778 pub fn emit_down(&self) -> String {
779 format!(
780 "-- Rollback v{}: {}\n{}",
781 self.version,
782 self.description,
783 self.down_statements.join(";\n")
784 )
785 }
786}
787#[allow(dead_code)]
788pub struct SQLQueryFormatter {
789 pub indent_size: usize,
790 pub uppercase_keywords: bool,
791 pub max_line_length: usize,
792}
793impl SQLQueryFormatter {
794 #[allow(dead_code)]
795 pub fn new() -> Self {
796 SQLQueryFormatter {
797 indent_size: 2,
798 uppercase_keywords: true,
799 max_line_length: 80,
800 }
801 }
802 #[allow(dead_code)]
803 pub fn format(&self, sql: &str) -> String {
804 let keywords = [
805 "SELECT", "FROM", "WHERE", "JOIN", "ON", "GROUP BY", "ORDER BY", "HAVING", "LIMIT",
806 "OFFSET",
807 ];
808 let mut result = sql.to_string();
809 if !self.uppercase_keywords {
810 result = result.to_lowercase();
811 }
812 for kw in &keywords {
813 let lower = kw.to_lowercase();
814 let target = if self.uppercase_keywords {
815 kw
816 } else {
817 &lower.as_str()
818 };
819 let pat = format!(" {} ", target);
820 let replacement = format!("\n{}{} ", " ".repeat(self.indent_size), target);
821 result = result.replace(&pat, &replacement);
822 }
823 result
824 }
825}
826#[allow(dead_code)]
827#[derive(Debug, Clone)]
828pub enum SQLTriggerTiming {
829 Before,
830 After,
831 InsteadOf,
832}
833#[allow(dead_code)]
834#[derive(Debug, Clone)]
835pub struct SQLIndexInfo {
836 pub name: String,
837 pub columns: Vec<String>,
838 pub is_unique: bool,
839 pub is_primary: bool,
840 pub index_type: String,
841}
842#[allow(dead_code)]
843pub struct SQLAnalyzeBuilder {
844 pub table: String,
845 pub columns: Vec<String>,
846 pub verbose: bool,
847}
848impl SQLAnalyzeBuilder {
849 #[allow(dead_code)]
850 pub fn new(table: impl Into<String>) -> Self {
851 SQLAnalyzeBuilder {
852 table: table.into(),
853 columns: Vec::new(),
854 verbose: false,
855 }
856 }
857 #[allow(dead_code)]
858 pub fn verbose(mut self) -> Self {
859 self.verbose = true;
860 self
861 }
862 #[allow(dead_code)]
863 pub fn column(mut self, col: impl Into<String>) -> Self {
864 self.columns.push(col.into());
865 self
866 }
867 #[allow(dead_code)]
868 pub fn build(&self) -> String {
869 let verbose = if self.verbose { "VERBOSE " } else { "" };
870 if self.columns.is_empty() {
871 format!("ANALYZE {}{}", verbose, self.table)
872 } else {
873 format!(
874 "ANALYZE {}{} ({})",
875 verbose,
876 self.table,
877 self.columns.join(", ")
878 )
879 }
880 }
881}
882#[allow(dead_code)]
883pub struct SQLTypeMapper {
884 pub source_dialect: SQLDialect,
885 pub target_dialect: SQLDialect,
886}
887impl SQLTypeMapper {
888 #[allow(dead_code)]
889 pub fn new(source: SQLDialect, target: SQLDialect) -> Self {
890 SQLTypeMapper {
891 source_dialect: source,
892 target_dialect: target,
893 }
894 }
895 #[allow(dead_code)]
896 pub fn map_integer(&self) -> &'static str {
897 match self.target_dialect {
898 SQLDialect::PostgreSQL => "INTEGER",
899 SQLDialect::MySQL => "INT",
900 SQLDialect::SQLite => "INTEGER",
901 SQLDialect::MSSQL => "INT",
902 }
903 }
904 #[allow(dead_code)]
905 pub fn map_bigint(&self) -> &'static str {
906 match self.target_dialect {
907 SQLDialect::PostgreSQL => "BIGINT",
908 SQLDialect::MySQL => "BIGINT",
909 SQLDialect::SQLite => "INTEGER",
910 SQLDialect::MSSQL => "BIGINT",
911 }
912 }
913 #[allow(dead_code)]
914 pub fn map_text(&self) -> &'static str {
915 match self.target_dialect {
916 SQLDialect::PostgreSQL | SQLDialect::SQLite => "TEXT",
917 SQLDialect::MySQL => "LONGTEXT",
918 SQLDialect::MSSQL => "NVARCHAR(MAX)",
919 }
920 }
921 #[allow(dead_code)]
922 pub fn map_boolean(&self) -> &'static str {
923 match self.target_dialect {
924 SQLDialect::PostgreSQL | SQLDialect::SQLite => "BOOLEAN",
925 SQLDialect::MySQL => "TINYINT(1)",
926 SQLDialect::MSSQL => "BIT",
927 }
928 }
929 #[allow(dead_code)]
930 pub fn map_timestamp(&self) -> &'static str {
931 match self.target_dialect {
932 SQLDialect::PostgreSQL => "TIMESTAMPTZ",
933 SQLDialect::MySQL => "DATETIME",
934 SQLDialect::SQLite => "DATETIME",
935 SQLDialect::MSSQL => "DATETIME2",
936 }
937 }
938 #[allow(dead_code)]
939 pub fn map_json(&self) -> &'static str {
940 match self.target_dialect {
941 SQLDialect::PostgreSQL => "JSONB",
942 SQLDialect::MySQL => "JSON",
943 SQLDialect::SQLite => "TEXT",
944 SQLDialect::MSSQL => "NVARCHAR(MAX)",
945 }
946 }
947}
948#[derive(Debug, Clone)]
950pub enum SQLStmt {
951 Select {
952 cols: Vec<String>,
953 from: String,
954 where_: Option<SQLExpr>,
955 limit: Option<usize>,
956 },
957 Insert {
958 table: String,
959 values: Vec<SQLExpr>,
960 },
961 Update {
962 table: String,
963 set_col: String,
964 set_val: SQLExpr,
965 where_: Option<SQLExpr>,
966 },
967 Delete {
968 table: String,
969 where_: Option<SQLExpr>,
970 },
971 CreateTable(SQLTable),
972 DropTable(String),
973}
974pub struct SQLBackend {
976 pub dialect: SQLDialect,
977}
978impl SQLBackend {
979 pub fn new(dialect: SQLDialect) -> Self {
981 SQLBackend { dialect }
982 }
983 pub fn emit_type(&self, ty: &SQLType) -> &str {
985 match self.dialect {
986 SQLDialect::PostgreSQL => match ty {
987 SQLType::Integer => "INTEGER",
988 SQLType::Real => "DOUBLE PRECISION",
989 SQLType::Text => "TEXT",
990 SQLType::Blob => "BYTEA",
991 SQLType::Null => "NULL",
992 SQLType::Boolean => "BOOLEAN",
993 SQLType::Timestamp => "TIMESTAMP",
994 },
995 SQLDialect::MySQL => match ty {
996 SQLType::Integer => "INT",
997 SQLType::Real => "DOUBLE",
998 SQLType::Text => "TEXT",
999 SQLType::Blob => "BLOB",
1000 SQLType::Null => "NULL",
1001 SQLType::Boolean => "TINYINT(1)",
1002 SQLType::Timestamp => "DATETIME",
1003 },
1004 SQLDialect::MSSQL => match ty {
1005 SQLType::Integer => "INT",
1006 SQLType::Real => "FLOAT",
1007 SQLType::Text => "NVARCHAR(MAX)",
1008 SQLType::Blob => "VARBINARY(MAX)",
1009 SQLType::Null => "NULL",
1010 SQLType::Boolean => "BIT",
1011 SQLType::Timestamp => "DATETIME2",
1012 },
1013 SQLDialect::SQLite => match ty {
1014 SQLType::Integer => "INTEGER",
1015 SQLType::Real => "REAL",
1016 SQLType::Text => "TEXT",
1017 SQLType::Blob => "BLOB",
1018 SQLType::Null => "NULL",
1019 SQLType::Boolean => "INTEGER",
1020 SQLType::Timestamp => "TEXT",
1021 },
1022 }
1023 }
1024 pub fn emit_expr(&self, expr: &SQLExpr) -> String {
1026 match expr {
1027 SQLExpr::Column(name) => name.clone(),
1028 SQLExpr::Literal(val) => val.clone(),
1029 SQLExpr::BinOp(lhs, op, rhs) => {
1030 format!("({} {} {})", self.emit_expr(lhs), op, self.emit_expr(rhs))
1031 }
1032 SQLExpr::FuncCall(func, args) => {
1033 let arg_strs: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
1034 format!("{}({})", func, arg_strs.join(", "))
1035 }
1036 }
1037 }
1038 pub fn emit_stmt(&self, stmt: &SQLStmt) -> String {
1040 match stmt {
1041 SQLStmt::Select {
1042 cols,
1043 from,
1044 where_,
1045 limit,
1046 } => {
1047 let col_str = if cols.is_empty() {
1048 "*".to_string()
1049 } else {
1050 cols.join(", ")
1051 };
1052 let mut s = format!("SELECT {} FROM {}", col_str, from);
1053 if let Some(cond) = where_ {
1054 s.push_str(&format!(" WHERE {}", self.emit_expr(cond)));
1055 }
1056 if let Some(n) = limit {
1057 match self.dialect {
1058 SQLDialect::MSSQL => {
1059 s = format!("SELECT TOP {} {} FROM {}", n, col_str, from);
1060 if let Some(cond) = where_ {
1061 s.push_str(&format!(" WHERE {}", self.emit_expr(cond)));
1062 }
1063 }
1064 _ => s.push_str(&format!(" LIMIT {}", n)),
1065 }
1066 }
1067 s.push(';');
1068 s
1069 }
1070 SQLStmt::Insert { table, values } => {
1071 let val_strs: Vec<String> = values.iter().map(|v| self.emit_expr(v)).collect();
1072 format!("INSERT INTO {} VALUES ({});", table, val_strs.join(", "))
1073 }
1074 SQLStmt::Update {
1075 table,
1076 set_col,
1077 set_val,
1078 where_,
1079 } => {
1080 let mut s = format!(
1081 "UPDATE {} SET {} = {}",
1082 table,
1083 set_col,
1084 self.emit_expr(set_val)
1085 );
1086 if let Some(cond) = where_ {
1087 s.push_str(&format!(" WHERE {}", self.emit_expr(cond)));
1088 }
1089 s.push(';');
1090 s
1091 }
1092 SQLStmt::Delete { table, where_ } => {
1093 let mut s = format!("DELETE FROM {}", table);
1094 if let Some(cond) = where_ {
1095 s.push_str(&format!(" WHERE {}", self.emit_expr(cond)));
1096 }
1097 s.push(';');
1098 s
1099 }
1100 SQLStmt::CreateTable(table) => self.create_table_stmt(table),
1101 SQLStmt::DropTable(name) => format!("DROP TABLE IF EXISTS {};", name),
1102 }
1103 }
1104 pub fn create_table_stmt(&self, table: &SQLTable) -> String {
1106 let cols: Vec<String> = table
1107 .columns
1108 .iter()
1109 .map(|col| {
1110 let mut def = format!("{} {}", col.name, self.emit_type(&col.ty));
1111 if col.primary_key {
1112 def.push_str(" PRIMARY KEY");
1113 }
1114 if col.not_null {
1115 def.push_str(" NOT NULL");
1116 }
1117 def
1118 })
1119 .collect();
1120 format!(
1121 "CREATE TABLE IF NOT EXISTS {} (\n {}\n);",
1122 table.name,
1123 cols.join(",\n ")
1124 )
1125 }
1126 pub fn schema_for_type(&self, type_name: &str) -> SQLTable {
1128 SQLTable {
1129 name: type_name.to_ascii_lowercase(),
1130 columns: vec![
1131 SQLColumn {
1132 name: "id".to_string(),
1133 ty: SQLType::Integer,
1134 not_null: true,
1135 primary_key: true,
1136 },
1137 SQLColumn {
1138 name: "name".to_string(),
1139 ty: SQLType::Text,
1140 not_null: true,
1141 primary_key: false,
1142 },
1143 SQLColumn {
1144 name: "created_at".to_string(),
1145 ty: SQLType::Timestamp,
1146 not_null: false,
1147 primary_key: false,
1148 },
1149 ],
1150 }
1151 }
1152 pub fn select_all(&self, table: &str) -> String {
1154 self.emit_stmt(&SQLStmt::Select {
1155 cols: vec![],
1156 from: table.to_string(),
1157 where_: None,
1158 limit: None,
1159 })
1160 }
1161 pub fn select_limit(&self, table: &str, n: usize) -> String {
1163 self.emit_stmt(&SQLStmt::Select {
1164 cols: vec![],
1165 from: table.to_string(),
1166 where_: None,
1167 limit: Some(n),
1168 })
1169 }
1170 pub fn insert_placeholders(&self, table: &str, col_count: usize) -> String {
1172 let placeholders: Vec<String> = (1..=col_count)
1173 .map(|i| match self.dialect {
1174 SQLDialect::PostgreSQL => format!("${}", i),
1175 _ => "?".to_string(),
1176 })
1177 .collect();
1178 format!(
1179 "INSERT INTO {} VALUES ({});",
1180 table,
1181 placeholders.join(", ")
1182 )
1183 }
1184}
1185#[derive(Debug, Clone)]
1187pub enum SQLExpr {
1188 Column(String),
1189 Literal(String),
1190 BinOp(Box<SQLExpr>, String, Box<SQLExpr>),
1191 FuncCall(String, Vec<SQLExpr>),
1192}
1193#[allow(dead_code)]
1194#[derive(Debug, Clone)]
1195pub struct SQLInsertBuilder {
1196 pub table: String,
1197 pub columns: Vec<String>,
1198 pub rows: Vec<Vec<String>>,
1199 pub on_conflict: Option<SQLConflictAction>,
1200 pub returning: Vec<String>,
1201}
1202impl SQLInsertBuilder {
1203 #[allow(dead_code)]
1204 pub fn new(table: impl Into<String>) -> Self {
1205 SQLInsertBuilder {
1206 table: table.into(),
1207 columns: Vec::new(),
1208 rows: Vec::new(),
1209 on_conflict: None,
1210 returning: Vec::new(),
1211 }
1212 }
1213 #[allow(dead_code)]
1214 pub fn column(mut self, col: impl Into<String>) -> Self {
1215 self.columns.push(col.into());
1216 self
1217 }
1218 #[allow(dead_code)]
1219 pub fn values(mut self, vals: Vec<String>) -> Self {
1220 self.rows.push(vals);
1221 self
1222 }
1223 #[allow(dead_code)]
1224 pub fn on_conflict(mut self, action: SQLConflictAction) -> Self {
1225 self.on_conflict = Some(action);
1226 self
1227 }
1228 #[allow(dead_code)]
1229 pub fn returning(mut self, col: impl Into<String>) -> Self {
1230 self.returning.push(col.into());
1231 self
1232 }
1233 #[allow(dead_code)]
1234 pub fn build(&self) -> String {
1235 let mut out = format!("INSERT INTO {}", self.table);
1236 if !self.columns.is_empty() {
1237 out.push_str(&format!(" ({})", self.columns.join(", ")));
1238 }
1239 out.push_str(" VALUES");
1240 let rows: Vec<String> = self
1241 .rows
1242 .iter()
1243 .map(|row| format!("({})", row.join(", ")))
1244 .collect();
1245 out.push(' ');
1246 out.push_str(&rows.join(", "));
1247 if let Some(ref action) = self.on_conflict {
1248 match action {
1249 SQLConflictAction::Ignore => out.push_str(" ON CONFLICT DO NOTHING"),
1250 SQLConflictAction::Replace => out.push_str(" OR REPLACE"),
1251 SQLConflictAction::Update(sets) => {
1252 out.push_str(" ON CONFLICT DO UPDATE SET ");
1253 let parts: Vec<String> =
1254 sets.iter().map(|(k, v)| format!("{} = {}", k, v)).collect();
1255 out.push_str(&parts.join(", "));
1256 }
1257 }
1258 }
1259 if !self.returning.is_empty() {
1260 out.push_str(&format!(" RETURNING {}", self.returning.join(", ")));
1261 }
1262 out
1263 }
1264}
1265#[allow(dead_code)]
1266pub struct SQLQueryOptimizer {
1267 pub stats: std::collections::HashMap<String, u64>,
1268}
1269impl SQLQueryOptimizer {
1270 #[allow(dead_code)]
1271 pub fn new() -> Self {
1272 SQLQueryOptimizer {
1273 stats: std::collections::HashMap::new(),
1274 }
1275 }
1276 #[allow(dead_code)]
1277 pub fn add_table_stats(&mut self, table: impl Into<String>, rows: u64) {
1278 self.stats.insert(table.into(), rows);
1279 }
1280 #[allow(dead_code)]
1281 pub fn estimate_join_cost(&self, left: &str, right: &str) -> f64 {
1282 let left_rows = self.stats.get(left).copied().unwrap_or(1000);
1283 let right_rows = self.stats.get(right).copied().unwrap_or(1000);
1284 (left_rows as f64) * (right_rows as f64).log2().max(1.0)
1285 }
1286 #[allow(dead_code)]
1287 pub fn suggest_indexes(&self, select: &SQLSelectBuilder) -> Vec<String> {
1288 let mut suggestions = Vec::new();
1289 if let Some(ref table) = select.from {
1290 if let Some(ref w) = select.where_clause {
1291 if w.contains("id") {
1292 suggestions.push(format!(
1293 "CREATE INDEX idx_{}_id ON {} (id)",
1294 table.to_lowercase(),
1295 table
1296 ));
1297 }
1298 if w.contains("created_at") {
1299 suggestions.push(format!(
1300 "CREATE INDEX idx_{}_created_at ON {} (created_at)",
1301 table.to_lowercase(),
1302 table
1303 ));
1304 }
1305 }
1306 }
1307 suggestions
1308 }
1309}
1310#[allow(dead_code)]
1311#[derive(Debug, Clone)]
1312pub enum SQLForeignKeyAction {
1313 Cascade,
1314 SetNull,
1315 SetDefault,
1316 Restrict,
1317 NoAction,
1318}
1319impl SQLForeignKeyAction {
1320 #[allow(dead_code)]
1321 pub fn keyword(&self) -> &str {
1322 match self {
1323 SQLForeignKeyAction::Cascade => "CASCADE",
1324 SQLForeignKeyAction::SetNull => "SET NULL",
1325 SQLForeignKeyAction::SetDefault => "SET DEFAULT",
1326 SQLForeignKeyAction::Restrict => "RESTRICT",
1327 SQLForeignKeyAction::NoAction => "NO ACTION",
1328 }
1329 }
1330}
1331#[allow(dead_code)]
1332#[derive(Debug, Clone)]
1333pub struct SQLStoredProcedure {
1334 pub name: String,
1335 pub params: Vec<SQLFunctionParam>,
1336 pub body: String,
1337 pub language: String,
1338 pub is_function: bool,
1339 pub return_type: Option<String>,
1340}
1341impl SQLStoredProcedure {
1342 #[allow(dead_code)]
1343 pub fn function(name: impl Into<String>, return_type: impl Into<String>) -> Self {
1344 SQLStoredProcedure {
1345 name: name.into(),
1346 params: Vec::new(),
1347 body: String::new(),
1348 language: "plpgsql".to_string(),
1349 is_function: true,
1350 return_type: Some(return_type.into()),
1351 }
1352 }
1353 #[allow(dead_code)]
1354 pub fn procedure(name: impl Into<String>) -> Self {
1355 SQLStoredProcedure {
1356 name: name.into(),
1357 params: Vec::new(),
1358 body: String::new(),
1359 language: "plpgsql".to_string(),
1360 is_function: false,
1361 return_type: None,
1362 }
1363 }
1364 #[allow(dead_code)]
1365 pub fn param(mut self, name: impl Into<String>, ty: impl Into<String>) -> Self {
1366 self.params.push(SQLFunctionParam {
1367 name: name.into(),
1368 ty: ty.into(),
1369 direction: SQLParamDirection::In,
1370 default_value: None,
1371 });
1372 self
1373 }
1374 #[allow(dead_code)]
1375 pub fn body(mut self, body: impl Into<String>) -> Self {
1376 self.body = body.into();
1377 self
1378 }
1379 #[allow(dead_code)]
1380 pub fn language(mut self, lang: impl Into<String>) -> Self {
1381 self.language = lang.into();
1382 self
1383 }
1384 #[allow(dead_code)]
1385 pub fn emit(&self) -> String {
1386 let kind = if self.is_function {
1387 "FUNCTION"
1388 } else {
1389 "PROCEDURE"
1390 };
1391 let params: Vec<String> = self
1392 .params
1393 .iter()
1394 .map(|p| format!("{} {} {}", p.direction.keyword(), p.name, p.ty))
1395 .collect();
1396 let mut out = format!(
1397 "CREATE OR REPLACE {} {}({})\n",
1398 kind,
1399 self.name,
1400 params.join(", ")
1401 );
1402 if let Some(ref ret) = self.return_type {
1403 out.push_str(&format!("RETURNS {}\n", ret));
1404 }
1405 out.push_str(&format!(
1406 "LANGUAGE {}\nAS $$\n{}\n$$;",
1407 self.language, self.body
1408 ));
1409 out
1410 }
1411}
1412#[allow(dead_code)]
1413#[derive(Debug, Clone)]
1414pub struct SQLJoin {
1415 pub join_type: SQLJoinType,
1416 pub table: String,
1417 pub alias: Option<String>,
1418 pub condition: Option<String>,
1419}
1420impl SQLJoin {
1421 #[allow(dead_code)]
1422 pub fn inner(table: impl Into<String>) -> Self {
1423 SQLJoin {
1424 join_type: SQLJoinType::Inner,
1425 table: table.into(),
1426 alias: None,
1427 condition: None,
1428 }
1429 }
1430 #[allow(dead_code)]
1431 pub fn left(table: impl Into<String>) -> Self {
1432 SQLJoin {
1433 join_type: SQLJoinType::Left,
1434 table: table.into(),
1435 alias: None,
1436 condition: None,
1437 }
1438 }
1439 #[allow(dead_code)]
1440 pub fn on(mut self, condition: impl Into<String>) -> Self {
1441 self.condition = Some(condition.into());
1442 self
1443 }
1444 #[allow(dead_code)]
1445 pub fn alias(mut self, alias: impl Into<String>) -> Self {
1446 self.alias = Some(alias.into());
1447 self
1448 }
1449 #[allow(dead_code)]
1450 pub fn emit(&self) -> String {
1451 let mut out = format!("{} {}", self.join_type.keyword(), self.table);
1452 if let Some(ref a) = self.alias {
1453 out.push_str(&format!(" AS {}", a));
1454 }
1455 if let Some(ref cond) = self.condition {
1456 out.push_str(&format!(" ON {}", cond));
1457 }
1458 out
1459 }
1460}
1461#[allow(dead_code)]
1462pub struct SQLWithQuery {
1463 pub ctes: Vec<SQLCommonTableExpression>,
1464 pub final_query: SQLSelectBuilder,
1465}
1466impl SQLWithQuery {
1467 #[allow(dead_code)]
1468 pub fn new(final_query: SQLSelectBuilder) -> Self {
1469 SQLWithQuery {
1470 ctes: Vec::new(),
1471 final_query,
1472 }
1473 }
1474 #[allow(dead_code)]
1475 pub fn with(mut self, cte: SQLCommonTableExpression) -> Self {
1476 self.ctes.push(cte);
1477 self
1478 }
1479 #[allow(dead_code)]
1480 pub fn build(&self) -> String {
1481 let recursive = if self.ctes.iter().any(|c| c.recursive) {
1482 " RECURSIVE"
1483 } else {
1484 ""
1485 };
1486 let cte_parts: Vec<String> = self.ctes.iter().map(|c| c.emit_cte_part()).collect();
1487 format!(
1488 "WITH{} {} {}",
1489 recursive,
1490 cte_parts.join(", "),
1491 self.final_query.build()
1492 )
1493 }
1494}
1495#[allow(dead_code)]
1496#[derive(Debug, Clone)]
1497pub struct SQLColumnInfo {
1498 pub name: String,
1499 pub ordinal_position: u32,
1500 pub data_type: String,
1501 pub is_nullable: bool,
1502 pub column_default: Option<String>,
1503 pub is_primary_key: bool,
1504 pub is_unique: bool,
1505}
1506#[allow(dead_code)]
1507#[derive(Debug, Clone)]
1508pub enum SQLQueryPlanNode {
1509 SeqScan {
1510 table: String,
1511 cost: f64,
1512 rows: u64,
1513 },
1514 IndexScan {
1515 table: String,
1516 index: String,
1517 cost: f64,
1518 rows: u64,
1519 },
1520 HashJoin {
1521 cost: f64,
1522 rows: u64,
1523 },
1524 MergeJoin {
1525 cost: f64,
1526 rows: u64,
1527 },
1528 NestedLoop {
1529 cost: f64,
1530 rows: u64,
1531 },
1532 Sort {
1533 key: Vec<String>,
1534 cost: f64,
1535 },
1536 Aggregate {
1537 function: String,
1538 cost: f64,
1539 },
1540 Hash {
1541 cost: f64,
1542 },
1543}
1544impl SQLQueryPlanNode {
1545 #[allow(dead_code)]
1546 pub fn cost(&self) -> f64 {
1547 match self {
1548 SQLQueryPlanNode::SeqScan { cost, .. }
1549 | SQLQueryPlanNode::IndexScan { cost, .. }
1550 | SQLQueryPlanNode::HashJoin { cost, .. }
1551 | SQLQueryPlanNode::MergeJoin { cost, .. }
1552 | SQLQueryPlanNode::NestedLoop { cost, .. }
1553 | SQLQueryPlanNode::Sort { cost, .. }
1554 | SQLQueryPlanNode::Aggregate { cost, .. }
1555 | SQLQueryPlanNode::Hash { cost, .. } => *cost,
1556 }
1557 }
1558 #[allow(dead_code)]
1559 pub fn node_type(&self) -> &str {
1560 match self {
1561 SQLQueryPlanNode::SeqScan { .. } => "Seq Scan",
1562 SQLQueryPlanNode::IndexScan { .. } => "Index Scan",
1563 SQLQueryPlanNode::HashJoin { .. } => "Hash Join",
1564 SQLQueryPlanNode::MergeJoin { .. } => "Merge Join",
1565 SQLQueryPlanNode::NestedLoop { .. } => "Nested Loop",
1566 SQLQueryPlanNode::Sort { .. } => "Sort",
1567 SQLQueryPlanNode::Aggregate { .. } => "Aggregate",
1568 SQLQueryPlanNode::Hash { .. } => "Hash",
1569 }
1570 }
1571}
1572#[allow(dead_code)]
1573pub struct SQLQueryPlan {
1574 pub nodes: Vec<SQLQueryPlanNode>,
1575 pub total_cost: f64,
1576 pub estimated_rows: u64,
1577}
1578impl SQLQueryPlan {
1579 #[allow(dead_code)]
1580 pub fn new() -> Self {
1581 SQLQueryPlan {
1582 nodes: Vec::new(),
1583 total_cost: 0.0,
1584 estimated_rows: 0,
1585 }
1586 }
1587 #[allow(dead_code)]
1588 pub fn add_node(&mut self, node: SQLQueryPlanNode) {
1589 self.total_cost += node.cost();
1590 self.nodes.push(node);
1591 }
1592 #[allow(dead_code)]
1593 pub fn has_seq_scan(&self) -> bool {
1594 self.nodes
1595 .iter()
1596 .any(|n| matches!(n, SQLQueryPlanNode::SeqScan { .. }))
1597 }
1598 #[allow(dead_code)]
1599 pub fn has_index_scan(&self) -> bool {
1600 self.nodes
1601 .iter()
1602 .any(|n| matches!(n, SQLQueryPlanNode::IndexScan { .. }))
1603 }
1604 #[allow(dead_code)]
1605 pub fn describe(&self) -> String {
1606 let mut out = format!(
1607 "Query Plan (total cost: {:.2}, rows: {}):\n",
1608 self.total_cost, self.estimated_rows
1609 );
1610 for (i, node) in self.nodes.iter().enumerate() {
1611 out.push_str(&format!(
1612 " {}: {} (cost: {:.2})\n",
1613 i + 1,
1614 node.node_type(),
1615 node.cost()
1616 ));
1617 }
1618 out
1619 }
1620}
1621#[allow(dead_code)]
1622#[derive(Debug, Clone)]
1623pub struct SQLFunctionParam {
1624 pub name: String,
1625 pub ty: String,
1626 pub direction: SQLParamDirection,
1627 pub default_value: Option<String>,
1628}
1629#[allow(dead_code)]
1630#[derive(Debug, Clone)]
1631pub struct SQLParameter {
1632 pub name: String,
1633 pub ty: SQLType,
1634 pub index: usize,
1635}
1636#[allow(dead_code)]
1637#[derive(Debug, Clone)]
1638pub struct SQLAlterTableBuilder {
1639 pub table: String,
1640 pub operations: Vec<SQLAlterOperation>,
1641}
1642impl SQLAlterTableBuilder {
1643 #[allow(dead_code)]
1644 pub fn new(table: impl Into<String>) -> Self {
1645 SQLAlterTableBuilder {
1646 table: table.into(),
1647 operations: Vec::new(),
1648 }
1649 }
1650 #[allow(dead_code)]
1651 pub fn add_column(mut self, col: SQLColumnDef) -> Self {
1652 self.operations.push(SQLAlterOperation::AddColumn(col));
1653 self
1654 }
1655 #[allow(dead_code)]
1656 pub fn drop_column(mut self, name: impl Into<String>) -> Self {
1657 self.operations
1658 .push(SQLAlterOperation::DropColumn(name.into()));
1659 self
1660 }
1661 #[allow(dead_code)]
1662 pub fn rename_column(mut self, from: impl Into<String>, to: impl Into<String>) -> Self {
1663 self.operations
1664 .push(SQLAlterOperation::RenameColumn(from.into(), to.into()));
1665 self
1666 }
1667 #[allow(dead_code)]
1668 pub fn build(&self, dialect: &SQLDialect) -> Vec<String> {
1669 self.operations
1670 .iter()
1671 .map(|op| match op {
1672 SQLAlterOperation::AddColumn(col) => {
1673 format!(
1674 "ALTER TABLE {} ADD COLUMN {}",
1675 self.table,
1676 col.emit(dialect)
1677 )
1678 }
1679 SQLAlterOperation::DropColumn(name) => {
1680 format!("ALTER TABLE {} DROP COLUMN {}", self.table, name)
1681 }
1682 SQLAlterOperation::RenameColumn(from, to) => {
1683 format!(
1684 "ALTER TABLE {} RENAME COLUMN {} TO {}",
1685 self.table, from, to
1686 )
1687 }
1688 SQLAlterOperation::AlterColumnType(col, ty) => {
1689 format!(
1690 "ALTER TABLE {} ALTER COLUMN {} TYPE {}",
1691 self.table, col, ty
1692 )
1693 }
1694 SQLAlterOperation::AddConstraint(c) => {
1695 format!("ALTER TABLE {} ADD {}", self.table, c.emit())
1696 }
1697 SQLAlterOperation::DropConstraint(name) => {
1698 format!("ALTER TABLE {} DROP CONSTRAINT {}", self.table, name)
1699 }
1700 SQLAlterOperation::RenameTable(new_name) => {
1701 format!("ALTER TABLE {} RENAME TO {}", self.table, new_name)
1702 }
1703 })
1704 .collect()
1705 }
1706}
1707#[allow(dead_code)]
1708#[derive(Debug, Clone)]
1709pub struct SQLWindowFunction {
1710 pub function: String,
1711 pub partition_by: Vec<String>,
1712 pub order_by: Vec<(String, bool)>,
1713 pub frame: Option<SQLWindowFrame>,
1714}
1715impl SQLWindowFunction {
1716 #[allow(dead_code)]
1717 pub fn new(function: impl Into<String>) -> Self {
1718 SQLWindowFunction {
1719 function: function.into(),
1720 partition_by: Vec::new(),
1721 order_by: Vec::new(),
1722 frame: None,
1723 }
1724 }
1725 #[allow(dead_code)]
1726 pub fn partition_by(mut self, col: impl Into<String>) -> Self {
1727 self.partition_by.push(col.into());
1728 self
1729 }
1730 #[allow(dead_code)]
1731 pub fn order_asc(mut self, col: impl Into<String>) -> Self {
1732 self.order_by.push((col.into(), true));
1733 self
1734 }
1735 #[allow(dead_code)]
1736 pub fn emit(&self) -> String {
1737 let mut out = format!("{} OVER (", self.function);
1738 if !self.partition_by.is_empty() {
1739 out.push_str(&format!("PARTITION BY {}", self.partition_by.join(", ")));
1740 }
1741 if !self.order_by.is_empty() {
1742 if !self.partition_by.is_empty() {
1743 out.push(' ');
1744 }
1745 let parts: Vec<String> = self
1746 .order_by
1747 .iter()
1748 .map(|(col, asc)| format!("{} {}", col, if *asc { "ASC" } else { "DESC" }))
1749 .collect();
1750 out.push_str(&format!("ORDER BY {}", parts.join(", ")));
1751 }
1752 out.push(')');
1753 out
1754 }
1755}
1756#[derive(Debug, Clone)]
1758pub struct SQLTable {
1759 pub name: String,
1760 pub columns: Vec<SQLColumn>,
1761}
1762#[allow(dead_code)]
1763#[derive(Debug, Clone)]
1764pub struct SQLSelectBuilder {
1765 pub columns: Vec<String>,
1766 pub from: Option<String>,
1767 pub joins: Vec<SQLJoin>,
1768 pub where_clause: Option<String>,
1769 pub group_by: Vec<String>,
1770 pub having: Option<String>,
1771 pub order_by: Vec<(String, bool)>,
1772 pub limit: Option<u64>,
1773 pub offset: Option<u64>,
1774 pub distinct: bool,
1775}
1776impl SQLSelectBuilder {
1777 #[allow(dead_code)]
1778 pub fn new() -> Self {
1779 SQLSelectBuilder {
1780 columns: Vec::new(),
1781 from: None,
1782 joins: Vec::new(),
1783 where_clause: None,
1784 group_by: Vec::new(),
1785 having: None,
1786 order_by: Vec::new(),
1787 limit: None,
1788 offset: None,
1789 distinct: false,
1790 }
1791 }
1792 #[allow(dead_code)]
1793 pub fn column(mut self, col: impl Into<String>) -> Self {
1794 self.columns.push(col.into());
1795 self
1796 }
1797 #[allow(dead_code)]
1798 pub fn from_table(mut self, table: impl Into<String>) -> Self {
1799 self.from = Some(table.into());
1800 self
1801 }
1802 #[allow(dead_code)]
1803 pub fn join(mut self, j: SQLJoin) -> Self {
1804 self.joins.push(j);
1805 self
1806 }
1807 #[allow(dead_code)]
1808 pub fn where_cond(mut self, cond: impl Into<String>) -> Self {
1809 self.where_clause = Some(cond.into());
1810 self
1811 }
1812 #[allow(dead_code)]
1813 pub fn group_by(mut self, col: impl Into<String>) -> Self {
1814 self.group_by.push(col.into());
1815 self
1816 }
1817 #[allow(dead_code)]
1818 pub fn order_asc(mut self, col: impl Into<String>) -> Self {
1819 self.order_by.push((col.into(), true));
1820 self
1821 }
1822 #[allow(dead_code)]
1823 pub fn order_desc(mut self, col: impl Into<String>) -> Self {
1824 self.order_by.push((col.into(), false));
1825 self
1826 }
1827 #[allow(dead_code)]
1828 pub fn limit(mut self, n: u64) -> Self {
1829 self.limit = Some(n);
1830 self
1831 }
1832 #[allow(dead_code)]
1833 pub fn offset(mut self, n: u64) -> Self {
1834 self.offset = Some(n);
1835 self
1836 }
1837 #[allow(dead_code)]
1838 pub fn distinct(mut self) -> Self {
1839 self.distinct = true;
1840 self
1841 }
1842 #[allow(dead_code)]
1843 pub fn build(&self) -> String {
1844 let mut out = String::from("SELECT");
1845 if self.distinct {
1846 out.push_str(" DISTINCT");
1847 }
1848 if self.columns.is_empty() {
1849 out.push_str(" *");
1850 } else {
1851 out.push(' ');
1852 out.push_str(&self.columns.join(", "));
1853 }
1854 if let Some(ref t) = self.from {
1855 out.push_str(&format!(" FROM {}", t));
1856 }
1857 for j in &self.joins {
1858 out.push(' ');
1859 out.push_str(&j.emit());
1860 }
1861 if let Some(ref w) = self.where_clause {
1862 out.push_str(&format!(" WHERE {}", w));
1863 }
1864 if !self.group_by.is_empty() {
1865 out.push_str(&format!(" GROUP BY {}", self.group_by.join(", ")));
1866 }
1867 if let Some(ref h) = self.having {
1868 out.push_str(&format!(" HAVING {}", h));
1869 }
1870 if !self.order_by.is_empty() {
1871 let parts: Vec<String> = self
1872 .order_by
1873 .iter()
1874 .map(|(col, asc)| format!("{} {}", col, if *asc { "ASC" } else { "DESC" }))
1875 .collect();
1876 out.push_str(&format!(" ORDER BY {}", parts.join(", ")));
1877 }
1878 if let Some(lim) = self.limit {
1879 out.push_str(&format!(" LIMIT {}", lim));
1880 }
1881 if let Some(off) = self.offset {
1882 out.push_str(&format!(" OFFSET {}", off));
1883 }
1884 out
1885 }
1886}
1887#[derive(Debug, Clone, PartialEq, Eq)]
1889pub enum SQLType {
1890 Integer,
1891 Real,
1892 Text,
1893 Blob,
1894 Null,
1895 Boolean,
1896 Timestamp,
1897}
1898#[allow(dead_code)]
1899#[derive(Debug, Clone)]
1900pub enum SQLConflictAction {
1901 Ignore,
1902 Replace,
1903 Update(Vec<(String, String)>),
1904}
1905#[allow(dead_code)]
1906#[derive(Debug, Clone)]
1907pub struct SQLDeleteBuilder {
1908 pub table: String,
1909 pub where_clause: Option<String>,
1910 pub returning: Vec<String>,
1911}
1912impl SQLDeleteBuilder {
1913 #[allow(dead_code)]
1914 pub fn new(table: impl Into<String>) -> Self {
1915 SQLDeleteBuilder {
1916 table: table.into(),
1917 where_clause: None,
1918 returning: Vec::new(),
1919 }
1920 }
1921 #[allow(dead_code)]
1922 pub fn where_cond(mut self, cond: impl Into<String>) -> Self {
1923 self.where_clause = Some(cond.into());
1924 self
1925 }
1926 #[allow(dead_code)]
1927 pub fn build(&self) -> String {
1928 let mut out = format!("DELETE FROM {}", self.table);
1929 if let Some(ref w) = self.where_clause {
1930 out.push_str(&format!(" WHERE {}", w));
1931 }
1932 out
1933 }
1934}
1935#[allow(dead_code)]
1936#[derive(Debug, Clone)]
1937pub enum SQLFrameBound {
1938 UnboundedPreceding,
1939 Preceding(u64),
1940 CurrentRow,
1941 Following(u64),
1942 UnboundedFollowing,
1943}
1944impl SQLFrameBound {
1945 #[allow(dead_code)]
1946 pub fn emit(&self) -> &str {
1947 match self {
1948 SQLFrameBound::UnboundedPreceding => "UNBOUNDED PRECEDING",
1949 SQLFrameBound::Preceding(_) => "PRECEDING",
1950 SQLFrameBound::CurrentRow => "CURRENT ROW",
1951 SQLFrameBound::Following(_) => "FOLLOWING",
1952 SQLFrameBound::UnboundedFollowing => "UNBOUNDED FOLLOWING",
1953 }
1954 }
1955}
1956#[allow(dead_code)]
1957pub struct SQLCommonTableExpression {
1958 pub name: String,
1959 pub columns: Vec<String>,
1960 pub query: SQLSelectBuilder,
1961 pub recursive: bool,
1962}
1963impl SQLCommonTableExpression {
1964 #[allow(dead_code)]
1965 pub fn new(name: impl Into<String>, query: SQLSelectBuilder) -> Self {
1966 SQLCommonTableExpression {
1967 name: name.into(),
1968 columns: Vec::new(),
1969 query,
1970 recursive: false,
1971 }
1972 }
1973 #[allow(dead_code)]
1974 pub fn recursive(mut self) -> Self {
1975 self.recursive = true;
1976 self
1977 }
1978 #[allow(dead_code)]
1979 pub fn with_column(mut self, col: impl Into<String>) -> Self {
1980 self.columns.push(col.into());
1981 self
1982 }
1983 #[allow(dead_code)]
1984 pub fn emit_cte_part(&self) -> String {
1985 let col_part = if self.columns.is_empty() {
1986 String::new()
1987 } else {
1988 format!("({})", self.columns.join(", "))
1989 };
1990 format!("{}{} AS ({})", self.name, col_part, self.query.build())
1991 }
1992}
1993#[allow(dead_code)]
1994#[derive(Debug, Clone)]
1995pub enum SQLIsolationLevel {
1996 ReadUncommitted,
1997 ReadCommitted,
1998 RepeatableRead,
1999 Serializable,
2000}
2001impl SQLIsolationLevel {
2002 #[allow(dead_code)]
2003 pub fn keyword(&self) -> &str {
2004 match self {
2005 SQLIsolationLevel::ReadUncommitted => "READ UNCOMMITTED",
2006 SQLIsolationLevel::ReadCommitted => "READ COMMITTED",
2007 SQLIsolationLevel::RepeatableRead => "REPEATABLE READ",
2008 SQLIsolationLevel::Serializable => "SERIALIZABLE",
2009 }
2010 }
2011}
2012#[allow(dead_code)]
2013#[derive(Debug, Clone)]
2014pub struct SQLViewBuilder {
2015 pub name: String,
2016 pub select: SQLSelectBuilder,
2017 pub replace: bool,
2018}
2019impl SQLViewBuilder {
2020 #[allow(dead_code)]
2021 pub fn new(name: impl Into<String>, select: SQLSelectBuilder) -> Self {
2022 SQLViewBuilder {
2023 name: name.into(),
2024 select,
2025 replace: false,
2026 }
2027 }
2028 #[allow(dead_code)]
2029 pub fn or_replace(mut self) -> Self {
2030 self.replace = true;
2031 self
2032 }
2033 #[allow(dead_code)]
2034 pub fn build(&self) -> String {
2035 let create = if self.replace {
2036 "CREATE OR REPLACE VIEW"
2037 } else {
2038 "CREATE VIEW"
2039 };
2040 format!("{} {} AS {}", create, self.name, self.select.build())
2041 }
2042}
2043#[allow(dead_code)]
2044pub struct SQLMigrationRunner {
2045 pub migrations: Vec<SQLMigration>,
2046 pub current_version: u32,
2047}
2048impl SQLMigrationRunner {
2049 #[allow(dead_code)]
2050 pub fn new() -> Self {
2051 SQLMigrationRunner {
2052 migrations: Vec::new(),
2053 current_version: 0,
2054 }
2055 }
2056 #[allow(dead_code)]
2057 pub fn add_migration(&mut self, m: SQLMigration) {
2058 self.migrations.push(m);
2059 self.migrations.sort_by_key(|m| m.version);
2060 }
2061 #[allow(dead_code)]
2062 pub fn pending_migrations(&self) -> Vec<&SQLMigration> {
2063 self.migrations
2064 .iter()
2065 .filter(|m| m.version > self.current_version)
2066 .collect()
2067 }
2068 #[allow(dead_code)]
2069 pub fn emit_pending_sql(&self) -> String {
2070 self.pending_migrations()
2071 .iter()
2072 .map(|m| m.emit_up())
2073 .collect::<Vec<_>>()
2074 .join("\n\n")
2075 }
2076}
2077#[derive(Debug, Clone)]
2079pub struct SQLColumn {
2080 pub name: String,
2081 pub ty: SQLType,
2082 pub not_null: bool,
2083 pub primary_key: bool,
2084}