use rust_ef::provider::ISqlGenerator;
#[derive(Debug, Clone)]
pub struct PostgresSqlGenerator;
impl PostgresSqlGenerator {
pub fn new() -> Self {
Self
}
}
impl Default for PostgresSqlGenerator {
fn default() -> Self {
Self::new()
}
}
impl ISqlGenerator for PostgresSqlGenerator {
fn select(&self, table: &str, columns: &[&str]) -> String {
let cols = if columns.is_empty() {
"*".to_string()
} else {
columns
.iter()
.map(|c| self.quote_identifier(c))
.collect::<Vec<_>>()
.join(", ")
};
format!("SELECT {} FROM {}", cols, self.quote_identifier(table))
}
fn insert(&self, table: &str, columns: &[&str], returning: bool) -> String {
let cols = columns
.iter()
.map(|c| self.quote_identifier(c))
.collect::<Vec<_>>()
.join(", ");
let placeholders: Vec<String> = (1..=columns.len())
.map(|i| self.parameter_placeholder(i))
.collect();
let returning_clause = if returning {
" RETURNING *".to_string()
} else {
String::new()
};
format!(
"INSERT INTO {} ({}) VALUES ({}){}",
self.quote_identifier(table),
cols,
placeholders.join(", "),
returning_clause
)
}
fn insert_batch(&self, table: &str, columns: &[&str], row_count: usize) -> String {
let cols = columns
.iter()
.map(|c| self.quote_identifier(c))
.collect::<Vec<_>>()
.join(", ");
let cols_per_row = columns.len();
let mut idx = 1usize;
let rows: Vec<String> = (0..row_count)
.map(|_| {
let ph: Vec<String> = (0..cols_per_row)
.map(|_| {
let p = self.parameter_placeholder(idx);
idx += 1;
p
})
.collect();
format!("({})", ph.join(", "))
})
.collect();
format!(
"INSERT INTO {} ({}) VALUES {} RETURNING *",
self.quote_identifier(table),
cols,
rows.join(", "),
)
}
fn upsert_batch(
&self,
table: &str,
columns: &[&str],
conflict_cols: &[&str],
row_count: usize,
) -> String {
let cols = columns
.iter()
.map(|c| self.quote_identifier(c))
.collect::<Vec<_>>()
.join(", ");
let cols_per_row = columns.len();
let mut idx = 1usize;
let rows: Vec<String> = (0..row_count)
.map(|_| {
let ph: Vec<String> = (0..cols_per_row)
.map(|_| {
let p = self.parameter_placeholder(idx);
idx += 1;
p
})
.collect();
format!("({})", ph.join(", "))
})
.collect();
let conflict = conflict_cols
.iter()
.map(|c| self.quote_identifier(c))
.collect::<Vec<_>>()
.join(", ");
let update_cols: Vec<&str> = columns
.iter()
.filter(|c| !conflict_cols.contains(c))
.copied()
.collect();
let sets = update_cols
.iter()
.map(|c| {
format!(
"{} = EXCLUDED.{}",
self.quote_identifier(c),
self.quote_identifier(c)
)
})
.collect::<Vec<_>>()
.join(", ");
format!(
"INSERT INTO {} ({}) VALUES {} ON CONFLICT({}) DO UPDATE SET {} RETURNING *",
self.quote_identifier(table),
cols,
rows.join(", "),
conflict,
sets,
)
}
fn update(&self, table: &str, set_columns: &[&str], where_clause: &str) -> String {
let sets: Vec<String> = set_columns
.iter()
.enumerate()
.map(|(i, c)| {
format!(
"{} = {}",
self.quote_identifier(c),
self.parameter_placeholder(i + 1)
)
})
.collect();
format!(
"UPDATE {} SET {} WHERE {}",
self.quote_identifier(table),
sets.join(", "),
where_clause
)
}
fn delete(&self, table: &str, where_clause: &str) -> String {
format!(
"DELETE FROM {} WHERE {}",
self.quote_identifier(table),
where_clause
)
}
fn create_table(&self, table: &str, columns: &[(String, String)]) -> String {
let col_defs: Vec<String> = columns
.iter()
.map(|(name, type_def)| format!("{} {}", self.quote_identifier(name), type_def))
.collect();
format!(
"CREATE TABLE {} (\n {}\n)",
self.quote_identifier(table),
col_defs.join(",\n ")
)
}
fn drop_table(&self, table: &str) -> String {
format!("DROP TABLE IF EXISTS {}", self.quote_identifier(table))
}
fn pagination(&self, skip: Option<usize>, take: Option<usize>) -> String {
match (skip, take) {
(Some(s), Some(t)) => format!("OFFSET {} LIMIT {}", s, t),
(Some(s), None) => format!("OFFSET {}", s),
(None, Some(t)) => format!("LIMIT {}", t),
(None, None) => String::new(),
}
}
fn parameter_placeholder(&self, index: usize) -> String {
format!("${}", index)
}
fn quote_identifier(&self, identifier: &str) -> String {
format!("\"{}\"", identifier)
}
fn auto_increment_syntax(&self) -> &'static str {
"SERIAL"
}
fn supports_returning(&self) -> bool {
true
}
fn uses_numbered_placeholders(&self) -> bool {
true
}
}