use std::marker::PhantomData;
use crate::dialect::{Dialect, SupportsOnConflict};
use crate::expr::{Column, ColumnKey, Value};
use crate::render::{QuerySink, Sink, render_ident};
use crate::scope::{BaseTable, Table};
use crate::statement::Statement;
use crate::update::Assignments;
#[diagnostic::on_unimplemented(
message = "`{Self}` isn't a value this column accepts",
label = "expected the column's own Rust type, or an `Option` of it"
)]
pub trait IntoColumnValue<V> {
fn into_column_value(self) -> V;
}
mod insertable {
pub trait Sealed {}
}
#[doc(hidden)]
pub use insertable::Sealed as InsertableSealed;
#[diagnostic::on_unimplemented(
message = "every column of `{Self}`'s table is generated, so only one row at a time can be inserted",
label = "`DEFAULT VALUES` is what SQL calls a row with nothing in it, and it names no columns to repeat",
note = "insert them one statement at a time"
)]
pub trait Insertable: InsertableSealed {}
#[diagnostic::on_unimplemented(
message = "column `{C}` hasn't been given a value yet",
label = "every column that is neither nullable nor defaulted needs one before `.build()`"
)]
pub trait Filled<C> {
#[doc(hidden)]
type Value;
#[doc(hidden)]
fn filled(self) -> Self::Value;
}
pub struct Missing<C>(std::marker::PhantomData<fn() -> C>);
impl<C> Missing<C> {
#[doc(hidden)]
pub const fn new() -> Self {
Missing(std::marker::PhantomData)
}
}
impl<C> Default for Missing<C> {
fn default() -> Self {
Missing::new()
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub enum Defaultable<T> {
#[default]
Default,
Value(T),
}
#[derive(Debug, Clone)]
pub enum InsertValue {
Value(Value),
Default,
}
impl<T: Into<Value>> From<Defaultable<T>> for InsertValue {
fn from(d: Defaultable<T>) -> Self {
match d {
Defaultable::Default => InsertValue::Default,
Defaultable::Value(v) => InsertValue::Value(v.into()),
}
}
}
pub trait InsertRow: private::Sealed {
type Table: Table;
type Values: InsertValues;
fn into_values(self) -> Self::Values;
}
mod insert_values {
pub trait Sealed {}
}
fn collect_values<R: InsertRow>(row: R) -> Vec<InsertValue> {
let mut out = Vec::new();
row.into_values().push_values(&mut out);
out
}
pub trait InsertValues: insert_values::Sealed {
#[doc(hidden)]
fn push_names(out: &mut Vec<&'static str>);
#[doc(hidden)]
fn push_values(self, out: &mut Vec<InsertValue>);
}
impl insert_values::Sealed for crate::row::RowNil {}
impl InsertValues for crate::row::RowNil {
fn push_names(_out: &mut Vec<&'static str>) {}
fn push_values(self, _out: &mut Vec<InsertValue>) {}
}
impl<C: crate::row::Named, Tail: InsertValues> insert_values::Sealed
for crate::row::RowCons<C, InsertValue, Tail>
{
}
impl<C: crate::row::Named, Tail: InsertValues> InsertValues
for crate::row::RowCons<C, InsertValue, Tail>
{
fn push_names(out: &mut Vec<&'static str>) {
out.push(<C as crate::row::Named>::NAME);
Tail::push_names(out);
}
fn push_values(self, out: &mut Vec<InsertValue>) {
let (value, tail) = self.into_cell();
out.push(value);
tail.push_values(out);
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` isn't an `ON CONFLICT` target for `{T}`",
label = "a column of that table, or a tuple of up to three of them"
)]
pub trait ConflictTarget<T: Table>: conflict_target::Sealed {
#[doc(hidden)]
fn column_names(&self) -> Vec<&'static str>;
}
mod conflict_target {
pub trait Sealed {}
impl<C: crate::expr::ColumnKey> Sealed for crate::expr::Column<C> {}
impl<A: crate::expr::ColumnKey> Sealed for (crate::expr::Column<A>,) {}
impl<A: crate::expr::ColumnKey, B: crate::expr::ColumnKey> Sealed
for (crate::expr::Column<A>, crate::expr::Column<B>)
{
}
impl<A: crate::expr::ColumnKey, B: crate::expr::ColumnKey, C: crate::expr::ColumnKey> Sealed
for (
crate::expr::Column<A>,
crate::expr::Column<B>,
crate::expr::Column<C>,
)
{
}
}
impl<C: ColumnKey> ConflictTarget<C::Table> for Column<C> {
fn column_names(&self) -> Vec<&'static str> {
vec![C::NAME]
}
}
macro_rules! conflict_target_tuple {
($($name:ident),+) => {
#[allow(non_snake_case)]
impl<T: Table, $($name: ColumnKey<Table = T>,)+> ConflictTarget<T> for ($(Column<$name>,)+) {
fn column_names(&self) -> Vec<&'static str> {
vec![$(<$name as crate::row::Named>::NAME),+]
}
}
};
}
conflict_target_tuple!(A);
conflict_target_tuple!(A, B);
conflict_target_tuple!(A, B, C);
enum ConflictAction<T> {
DoNothing,
DoUpdate(Assignments<T>),
}
struct ConflictClause<T> {
target: Vec<&'static str>,
action: ConflictAction<T>,
}
fn render_conflict_clause<D: Dialect, T>(clause: &ConflictClause<T>, sink: &mut dyn Sink) {
sink.text(" ON CONFLICT (");
for (i, c) in clause.target.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
render_ident::<D>(sink, c);
}
sink.ch(')');
match &clause.action {
ConflictAction::DoNothing => sink.text(" DO NOTHING"),
ConflictAction::DoUpdate(sets) => {
sink.text(" DO UPDATE SET ");
sets.render_into::<D>(sink);
}
}
}
mod private {
pub trait Sealed {}
}
#[doc(hidden)]
pub use private::Sealed as InsertRowSealed;
pub struct InsertSeed<D, T> {
_marker: PhantomData<fn() -> (D, T)>,
}
pub fn insert<D, T: BaseTable>(_table: T) -> InsertSeed<D, T> {
InsertSeed {
_marker: PhantomData,
}
}
impl<D, T: Table> InsertSeed<D, T> {
pub fn values<R: InsertRow<Table = T>>(self, row: R) -> Insert<D, R> {
Insert {
rows: vec![collect_values(row)],
on_conflict: None,
_marker: PhantomData,
}
}
pub fn values_all<R: InsertRow<Table = T> + Insertable>(
self,
rows: impl IntoIterator<Item = R>,
) -> Result<Insert<D, R>, NothingToInsert> {
let rows: Vec<_> = rows.into_iter().map(collect_values).collect();
if rows.is_empty() {
return Err(NothingToInsert);
}
Ok(Insert {
rows,
on_conflict: None,
_marker: PhantomData,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NothingToInsert;
impl std::fmt::Display for NothingToInsert {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an INSERT must have at least one row, but no rows were given")
}
}
impl std::error::Error for NothingToInsert {}
fn render_values_clause<D: Dialect, R: InsertRow>(
rows: &[Vec<InsertValue>],
on_conflict: &Option<ConflictClause<R::Table>>,
) -> QuerySink<D> {
let mut sink = QuerySink::<D>::new();
sink.text("INSERT INTO ");
render_ident::<D>(&mut sink, <R::Table as Table>::NAME);
let mut header = Vec::new();
<R::Values as InsertValues>::push_names(&mut header);
if header.is_empty() {
sink.text(D::INSERT_NO_COLUMNS);
if let Some(clause) = on_conflict {
render_conflict_clause::<D, _>(clause, &mut sink);
}
return sink;
}
sink.text(" (");
for (i, name) in header.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
render_ident::<D>(&mut sink, name);
}
sink.text(") VALUES ");
for (row_i, row) in rows.iter().enumerate() {
if row_i > 0 {
sink.text(", ");
}
sink.ch('(');
for (i, v) in row.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
match v {
InsertValue::Default => sink.text("DEFAULT"),
InsertValue::Value(v) => sink.bind(v),
}
}
sink.ch(')');
}
if let Some(clause) = on_conflict {
render_conflict_clause::<D, _>(clause, &mut sink);
}
sink
}
pub struct Insert<D, R: InsertRow> {
rows: Vec<Vec<InsertValue>>,
on_conflict: Option<ConflictClause<R::Table>>,
_marker: PhantomData<fn() -> (D, R)>,
}
impl<D, R: InsertRow + Insertable> Insert<D, R> {
pub fn values(mut self, row: R) -> Self {
self.rows.push(collect_values(row));
self
}
pub fn values_all(mut self, rows: impl IntoIterator<Item = R>) -> Self {
self.rows.extend(rows.into_iter().map(collect_values));
self
}
}
impl<D: Dialect, R: InsertRow> Insert<D, R> {
pub fn on_conflict_do_nothing(mut self, target: impl ConflictTarget<R::Table>) -> Self
where
D: SupportsOnConflict,
{
self.on_conflict = Some(ConflictClause {
target: target.column_names(),
action: ConflictAction::DoNothing,
});
self
}
pub fn on_conflict_do_update(
mut self,
target: impl ConflictTarget<R::Table>,
set: Assignments<R::Table>,
) -> Self
where
D: SupportsOnConflict,
{
self.on_conflict = Some(ConflictClause {
target: target.column_names(),
action: ConflictAction::DoUpdate(set),
});
self
}
}
impl<D: Dialect, R: InsertRow> crate::statement::private::Sealed for Insert<D, R> {}
impl<D: Dialect, R: InsertRow> Statement for Insert<D, R> {
type Dialect = D;
type Table = R::Table;
fn render(&self) -> QuerySink<D> {
render_values_clause::<D, R>(&self.rows, &self.on_conflict)
}
}