Skip to main content

rust_ef_postgres/
sql_generator.rs

1//! PostgreSQL-specific SQL dialect generator.
2
3use rust_ef::provider::ISqlGenerator;
4
5/// SQL generator that produces PostgreSQL-compatible SQL.
6#[derive(Debug, Clone)]
7pub struct PostgresSqlGenerator;
8
9impl PostgresSqlGenerator {
10    pub fn new() -> Self {
11        Self
12    }
13}
14
15impl Default for PostgresSqlGenerator {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20
21impl ISqlGenerator for PostgresSqlGenerator {
22    fn select(&self, table: &str, columns: &[&str]) -> String {
23        let cols = if columns.is_empty() {
24            "*".to_string()
25        } else {
26            columns
27                .iter()
28                .map(|c| self.quote_identifier(c))
29                .collect::<Vec<_>>()
30                .join(", ")
31        };
32        format!("SELECT {} FROM {}", cols, self.quote_identifier(table))
33    }
34
35    fn insert(&self, table: &str, columns: &[&str], returning: bool) -> String {
36        let cols = columns
37            .iter()
38            .map(|c| self.quote_identifier(c))
39            .collect::<Vec<_>>()
40            .join(", ");
41        let placeholders: Vec<String> = (1..=columns.len())
42            .map(|i| self.parameter_placeholder(i))
43            .collect();
44        let returning_clause = if returning {
45            " RETURNING *".to_string()
46        } else {
47            String::new()
48        };
49        format!(
50            "INSERT INTO {} ({}) VALUES ({}){}",
51            self.quote_identifier(table),
52            cols,
53            placeholders.join(", "),
54            returning_clause
55        )
56    }
57
58    fn insert_batch(&self, table: &str, columns: &[&str], row_count: usize) -> String {
59        let cols = columns
60            .iter()
61            .map(|c| self.quote_identifier(c))
62            .collect::<Vec<_>>()
63            .join(", ");
64        let cols_per_row = columns.len();
65        let mut idx = 1usize;
66        let rows: Vec<String> = (0..row_count)
67            .map(|_| {
68                let ph: Vec<String> = (0..cols_per_row)
69                    .map(|_| {
70                        let p = self.parameter_placeholder(idx);
71                        idx += 1;
72                        p
73                    })
74                    .collect();
75                format!("({})", ph.join(", "))
76            })
77            .collect();
78        format!(
79            "INSERT INTO {} ({}) VALUES {} RETURNING *",
80            self.quote_identifier(table),
81            cols,
82            rows.join(", "),
83        )
84    }
85
86    fn update(&self, table: &str, set_columns: &[&str], where_clause: &str) -> String {
87        let sets: Vec<String> = set_columns
88            .iter()
89            .enumerate()
90            .map(|(i, c)| {
91                format!(
92                    "{} = {}",
93                    self.quote_identifier(c),
94                    self.parameter_placeholder(i + 1)
95                )
96            })
97            .collect();
98        format!(
99            "UPDATE {} SET {} WHERE {}",
100            self.quote_identifier(table),
101            sets.join(", "),
102            where_clause
103        )
104    }
105
106    fn delete(&self, table: &str, where_clause: &str) -> String {
107        format!(
108            "DELETE FROM {} WHERE {}",
109            self.quote_identifier(table),
110            where_clause
111        )
112    }
113
114    fn create_table(&self, table: &str, columns: &[(String, String)]) -> String {
115        let col_defs: Vec<String> = columns
116            .iter()
117            .map(|(name, type_def)| format!("{} {}", self.quote_identifier(name), type_def))
118            .collect();
119        format!(
120            "CREATE TABLE {} (\n    {}\n)",
121            self.quote_identifier(table),
122            col_defs.join(",\n    ")
123        )
124    }
125
126    fn drop_table(&self, table: &str) -> String {
127        format!("DROP TABLE IF EXISTS {}", self.quote_identifier(table))
128    }
129
130    fn pagination(&self, skip: Option<usize>, take: Option<usize>) -> String {
131        match (skip, take) {
132            (Some(s), Some(t)) => format!("OFFSET {} LIMIT {}", s, t),
133            (Some(s), None) => format!("OFFSET {}", s),
134            (None, Some(t)) => format!("LIMIT {}", t),
135            (None, None) => String::new(),
136        }
137    }
138
139    fn parameter_placeholder(&self, index: usize) -> String {
140        format!("${}", index)
141    }
142
143    fn quote_identifier(&self, identifier: &str) -> String {
144        format!("\"{}\"", identifier)
145    }
146
147    fn auto_increment_syntax(&self) -> &'static str {
148        "SERIAL"
149    }
150}