use crate::core::{
AggregateQuery, BulkInsertQuery, BulkUpdateQuery, ConflictClause, CountQuery, DeleteQuery,
FieldType, InsertQuery, ModelSchema, Op, OrderClause, SearchClause, SelectQuery, UpdateQuery,
WhereExpr,
};
use super::writers::{
self, write_aggregate, write_bulk_insert, write_bulk_update_pg, write_count, write_delete,
write_insert, write_select, write_update, Sql,
};
use super::{CompiledStatement, Dialect, SqlError};
#[derive(Debug, Default, Clone, Copy)]
pub struct Postgres;
pub static DIALECT: &Postgres = &Postgres;
impl Dialect for Postgres {
fn name(&self) -> &'static str {
"postgres"
}
fn placeholder(&self, n: usize) -> String {
format!("${n}")
}
fn serial_type(&self, field_type: FieldType) -> &'static str {
match field_type {
FieldType::I32 => "SERIAL",
_ => "BIGSERIAL",
}
}
fn supports_concurrent_index(&self) -> bool {
true
}
fn supports_returning(&self) -> bool {
true
}
fn cast_aggregate_to_int(&self, expr: &str) -> String {
format!("{expr}::bigint")
}
fn cast_aggregate_to_float(&self, expr: &str) -> String {
format!("{expr}::double precision")
}
fn null_cast(&self, ty: FieldType) -> Option<&'static str> {
Some(match ty {
FieldType::I32 => "INTEGER",
FieldType::I64 => "BIGINT",
FieldType::F32 => "REAL",
FieldType::F64 => "DOUBLE PRECISION",
FieldType::Bool => "BOOLEAN",
FieldType::String => "TEXT",
FieldType::DateTime => "TIMESTAMPTZ",
FieldType::Date => "DATE",
FieldType::Uuid => "UUID",
FieldType::Json => "JSONB",
})
}
fn supports_op(&self, _op: Op) -> bool {
true
}
fn write_conflict_clause(
&self,
sql: &mut String,
conflict: &ConflictClause,
) -> Result<(), SqlError> {
match conflict {
ConflictClause::DoNothing => {
sql.push_str(" ON CONFLICT DO NOTHING");
}
ConflictClause::DoUpdate {
target,
update_columns,
} => {
sql.push_str(" ON CONFLICT (");
let mut first = true;
for col in target {
if !first {
sql.push_str(", ");
}
first = false;
write_pg_ident(sql, col);
}
sql.push_str(") DO UPDATE SET ");
let mut first = true;
for col in update_columns {
if !first {
sql.push_str(", ");
}
first = false;
write_pg_ident(sql, col);
sql.push_str(" = EXCLUDED.");
write_pg_ident(sql, col);
}
}
}
Ok(())
}
fn acquire_session_lock_sql(&self) -> Option<String> {
Some(format!("SELECT pg_advisory_lock({})", self.placeholder(1)))
}
fn release_session_lock_sql(&self) -> Option<String> {
Some(format!(
"SELECT pg_advisory_unlock({})",
self.placeholder(1)
))
}
fn acquire_xact_lock_sql(&self) -> Option<String> {
Some(format!(
"SELECT pg_advisory_xact_lock({})",
self.placeholder(1)
))
}
fn compile_select(&self, query: &SelectQuery) -> Result<CompiledStatement, SqlError> {
let mut b = Sql::new(self);
write_select(&mut b, query)?;
Ok(b.finish())
}
fn compile_count(&self, query: &CountQuery) -> Result<CompiledStatement, SqlError> {
let mut b = Sql::new(self);
write_count(&mut b, query)?;
Ok(b.finish())
}
fn compile_aggregate(&self, query: &AggregateQuery) -> Result<CompiledStatement, SqlError> {
let mut b = Sql::new(self);
write_aggregate(&mut b, query)?;
Ok(b.finish())
}
fn compile_insert(&self, query: &InsertQuery) -> Result<CompiledStatement, SqlError> {
let mut b = Sql::with_capacity(self, query.values.len());
write_insert(&mut b, query)?;
Ok(b.finish())
}
fn compile_bulk_insert(
&self,
query: &BulkInsertQuery,
) -> Result<CompiledStatement, SqlError> {
let mut b = Sql::with_capacity(self, query.columns.len() * query.rows.len());
write_bulk_insert(&mut b, query)?;
Ok(b.finish())
}
fn compile_update(&self, query: &UpdateQuery) -> Result<CompiledStatement, SqlError> {
let mut b = Sql::new(self);
write_update(&mut b, query)?;
Ok(b.finish())
}
fn compile_delete(&self, query: &DeleteQuery) -> Result<CompiledStatement, SqlError> {
let mut b = Sql::new(self);
write_delete(&mut b, query)?;
Ok(b.finish())
}
fn compile_bulk_update(&self, query: &BulkUpdateQuery) -> Result<CompiledStatement, SqlError> {
let mut b = Sql::new(self);
write_bulk_update_pg(&mut b, query)?;
Ok(b.finish())
}
}
fn write_pg_ident(sql: &mut String, name: &str) {
sql.push('"');
for ch in name.chars() {
if ch == '"' {
sql.push_str("\"\"");
} else {
sql.push(ch);
}
}
sql.push('"');
}
pub(crate) fn compile_where_order_tail(
where_clause: &WhereExpr,
search: Option<&SearchClause>,
order_by: &[OrderClause],
limit: Option<i64>,
offset: Option<i64>,
qualify_with: Option<&str>,
model: Option<&'static ModelSchema>,
) -> Result<CompiledStatement, SqlError> {
writers::compile_where_order_tail(
DIALECT,
where_clause,
search,
order_by,
limit,
offset,
qualify_with,
model,
)
}