use crate::traits::PostgresTable;
use crate::values::PostgresValue;
use core::marker::PhantomData;
use drizzle_core::builder::{
OnConflictBuilder as CoreOnConflictBuilder, OnConflictOutput, PostgresConflictTarget,
};
use drizzle_core::{ConflictTarget, NamedConstraint, SQL, ToSQL, Token};
pub use drizzle_core::builder::{
InsertDoUpdateSet, InsertInitial, InsertOnConflictSet, InsertReturningSet, InsertValuesSet,
};
pub type OnConflictBuilder<'a, S, T> = CoreOnConflictBuilder<
'a,
PostgresValue<'a>,
S,
T,
PostgresConflictTarget<'a, PostgresValue<'a>>,
PostgresOnConflictOutput,
>;
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct PostgresOnConflictOutput;
impl<'a, S, T> OnConflictOutput<'a, PostgresValue<'a>, S, T> for PostgresOnConflictOutput {
type OnConflictSet = InsertBuilder<'a, S, InsertOnConflictSet, T>;
type DoUpdateSet = InsertBuilder<'a, S, InsertDoUpdateSet, T>;
fn on_conflict(sql: SQL<'a, PostgresValue<'a>>) -> Self::OnConflictSet {
InsertBuilder {
sql,
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
fn do_update(sql: SQL<'a, PostgresValue<'a>>) -> Self::DoUpdateSet {
InsertBuilder {
sql,
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
pub type InsertBuilder<'a, Schema, State, Table, Marker = (), Row = ()> =
super::QueryBuilder<'a, Schema, State, Table, Marker, Row>;
type ReturningMarker<Table, Columns> = drizzle_core::Scoped<
<Columns as drizzle_core::IntoSelectTarget>::Marker,
drizzle_core::Cons<Table, drizzle_core::Nil>,
>;
type ReturningRow<Table, Columns> =
<<Columns as drizzle_core::IntoSelectTarget>::Marker as drizzle_core::ResolveRow<Table>>::Row;
type ReturningBuilder<'a, S, T, Columns> = InsertBuilder<
'a,
S,
InsertReturningSet,
T,
ReturningMarker<T, Columns>,
ReturningRow<T, Columns>,
>;
impl<'a, Schema, Table> InsertBuilder<'a, Schema, InsertInitial, Table>
where
Table: PostgresTable<'a>,
{
#[inline]
pub fn value<T>(
self,
value: Table::Insert<T>,
) -> InsertBuilder<'a, Schema, InsertValuesSet, Table> {
self.values([value])
}
#[inline]
pub fn values<I, T>(self, values: I) -> InsertBuilder<'a, Schema, InsertValuesSet, Table>
where
I: IntoIterator<Item = Table::Insert<T>>,
{
let sql = crate::helpers::values::<'a, Table, T>(values);
InsertBuilder {
sql: self.sql.append(sql),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
#[inline]
pub fn select<Q>(self, query: Q) -> InsertBuilder<'a, Schema, InsertValuesSet, Table>
where
Q: ToSQL<'a, PostgresValue<'a>>,
{
InsertBuilder {
sql: self.sql.append(query.into_sql()),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, T> InsertBuilder<'a, S, InsertValuesSet, T> {
pub fn on_conflict<C: ConflictTarget<T>>(self, target: C) -> OnConflictBuilder<'a, S, T> {
let columns = target.conflict_columns();
let target_sql = SQL::join(columns.iter().map(|c| SQL::ident(*c)), Token::COMMA);
OnConflictBuilder::new(self.sql, PostgresConflictTarget::columns(target_sql))
}
pub fn on_conflict_on_constraint<C: NamedConstraint<T>>(
self,
target: C,
) -> OnConflictBuilder<'a, S, T> {
OnConflictBuilder::new(
self.sql,
PostgresConflictTarget::constraint(target.constraint_name()),
)
}
#[must_use]
pub fn on_conflict_do_nothing(self) -> InsertBuilder<'a, S, InsertOnConflictSet, T> {
let conflict_sql = SQL::from_iter([Token::ON, Token::CONFLICT, Token::DO, Token::NOTHING]);
InsertBuilder {
sql: self.sql.append(conflict_sql),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
#[inline]
pub fn returning<Columns>(self, columns: Columns) -> ReturningBuilder<'a, S, T, Columns>
where
Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
Columns::Marker: drizzle_core::ResolveRow<T>,
{
let returning_sql = crate::helpers::returning(columns);
InsertBuilder {
sql: self.sql.append(returning_sql),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, T> InsertBuilder<'a, S, InsertOnConflictSet, T> {
#[inline]
pub fn returning<Columns>(self, columns: Columns) -> ReturningBuilder<'a, S, T, Columns>
where
Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
Columns::Marker: drizzle_core::ResolveRow<T>,
{
let returning_sql = crate::helpers::returning(columns);
InsertBuilder {
sql: self.sql.append(returning_sql),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
impl<'a, S, T> InsertBuilder<'a, S, InsertDoUpdateSet, T> {
pub fn r#where<E>(self, condition: E) -> InsertBuilder<'a, S, InsertOnConflictSet, T>
where
E: drizzle_core::expr::Expr<'a, PostgresValue<'a>>,
E::SQLType: drizzle_core::types::BooleanLike,
{
let sql = self
.sql
.push(Token::WHERE)
.append(condition.into_expr_sql());
InsertBuilder {
sql,
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
#[inline]
pub fn returning<Columns>(self, columns: Columns) -> ReturningBuilder<'a, S, T, Columns>
where
Columns: ToSQL<'a, PostgresValue<'a>> + drizzle_core::IntoSelectTarget,
Columns::Marker: drizzle_core::ResolveRow<T>,
{
let returning_sql = crate::helpers::returning(columns);
InsertBuilder {
sql: self.sql.append(returning_sql),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use drizzle_core::{SQL, ToSQL};
#[test]
fn test_insert_builder_creation() {
let builder = InsertBuilder::<(), InsertInitial, ()> {
sql: SQL::raw("INSERT INTO test"),
schema: PhantomData,
state: PhantomData,
table: PhantomData,
marker: PhantomData,
row: PhantomData,
grouped: PhantomData,
};
assert_eq!(builder.to_sql().sql(), "INSERT INTO test");
}
}