Skip to main content

oxilean_codegen/sql_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use 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/// SQL dialect selector.
212#[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/// SQL statements that the backend can emit.
949#[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}
974/// The SQL code generation backend.
975pub struct SQLBackend {
976    pub dialect: SQLDialect,
977}
978impl SQLBackend {
979    /// Create a new SQL backend for the given dialect.
980    pub fn new(dialect: SQLDialect) -> Self {
981        SQLBackend { dialect }
982    }
983    /// Emit a SQL type keyword for the current dialect.
984    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    /// Emit a SQL expression as a string.
1025    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    /// Emit a complete SQL statement.
1039    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    /// Build a CREATE TABLE statement from a `SQLTable`.
1105    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    /// Produce a default table schema for a named OxiLean type.
1127    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    /// Emit a simple SELECT * from a table.
1153    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    /// Emit a SELECT with a LIMIT clause.
1162    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    /// Emit a parameterised INSERT placeholder list (dialect-aware).
1171    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/// SQL expression AST.
1186#[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/// A table definition (schema).
1757#[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/// SQL column/expression types.
1888#[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/// A single column definition inside a CREATE TABLE.
2078#[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}