use std::marker::PhantomData;
use crate::dialect::{Dialect, SupportsOnConflict};
use crate::expr::{AssignsTo, Column, ColumnKey, Expr, ExprKind, IntoExpr, Value, Writable};
use crate::render::{Sink, render_ident};
use crate::scope::{BaseTable, Cons, Nil, NotNull, Superset, Table, TableSlot};
use crate::select::{Condition, Predicate};
use crate::statement::{Statement, WrittenTable};
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 a column list for `{T}`",
label = "a column of that table, or a tuple of up to three of them"
)]
pub trait ConflictColumns<T: Table>: conflict_target::ColumnsSealed<T> {
#[doc(hidden)]
fn column_names(&self) -> Vec<&'static str>;
}
#[diagnostic::on_unimplemented(
message = "`{Self}` isn't an `ON CONFLICT` target for `{T}`",
label = "a column of that table, a tuple of up to three of them, or `partial_index(..)` of either"
)]
pub trait ConflictTarget<T: Table>: conflict_target::Sealed<T> {
#[doc(hidden)]
fn into_target(self) -> Target;
}
#[doc(hidden)]
pub struct Target {
columns: Vec<&'static str>,
index_predicate: Option<ExprKind>,
}
mod conflict_target {
pub trait ColumnsSealed<T> {}
pub trait Sealed<T> {}
}
impl<C: ColumnKey> conflict_target::ColumnsSealed<C::Table> for Column<C> {}
impl<C: ColumnKey> conflict_target::Sealed<C::Table> for Column<C> {}
impl<C: ColumnKey> ConflictColumns<C::Table> for Column<C> {
fn column_names(&self) -> Vec<&'static str> {
vec![C::NAME]
}
}
impl<C: ColumnKey> ConflictTarget<C::Table> for Column<C> {
fn into_target(self) -> Target {
Target {
columns: self.column_names(),
index_predicate: None,
}
}
}
macro_rules! conflict_target_tuple {
($($name:ident),+) => {
impl<T: Table, $($name: ColumnKey<Table = T>,)+> conflict_target::ColumnsSealed<T>
for ($(Column<$name>,)+) {}
impl<T: Table, $($name: ColumnKey<Table = T>,)+> conflict_target::Sealed<T>
for ($(Column<$name>,)+) {}
#[allow(non_snake_case)]
impl<T: Table, $($name: ColumnKey<Table = T>,)+> ConflictColumns<T> for ($(Column<$name>,)+) {
fn column_names(&self) -> Vec<&'static str> {
vec![$(<$name as crate::row::Named>::NAME),+]
}
}
impl<T: Table, $($name: ColumnKey<Table = T>,)+> ConflictTarget<T> for ($(Column<$name>,)+) {
fn into_target(self) -> Target {
Target {
columns: self.column_names(),
index_predicate: None,
}
}
}
};
}
conflict_target_tuple!(A);
conflict_target_tuple!(A, B);
conflict_target_tuple!(A, B, C);
pub fn partial_index<T, Cols, E, Req, Idxs>(columns: Cols, index_predicate: E) -> PartialIndex<T>
where
T: Table,
Cols: ConflictColumns<T>,
E: crate::expr::IntoExpr<Req = Req>,
E::Sql: crate::expr::BoolLike,
WrittenTable<T>: crate::scope::Superset<Req, Idxs>,
{
PartialIndex {
target: Target {
columns: columns.column_names(),
index_predicate: Some(index_predicate.into_expr().kind),
},
_marker: PhantomData,
}
}
pub struct PartialIndex<T> {
target: Target,
_marker: PhantomData<fn() -> T>,
}
impl<T> conflict_target::Sealed<T> for PartialIndex<T> {}
impl<T: Table> ConflictTarget<T> for PartialIndex<T> {
fn into_target(self) -> Target {
self.target
}
}
pub(crate) const EXCLUDED: &str = "excluded";
pub struct Excluded<T>(PhantomData<fn() -> T>);
impl<T: Table> Table for Excluded<T> {
const NAME: &'static str = EXCLUDED;
}
pub type ConflictScope<T> = Cons<TableSlot<Excluded<T>, NotNull>, WrittenTable<T>>;
pub fn excluded<C: ColumnKey>(_column: Column<C>) -> Expr<Cons<Excluded<C::Table>, Nil>, C::Sql> {
Expr::from_kind(ExprKind::Excluded {
name: <C as crate::row::Named>::NAME,
})
}
pub struct ConflictUpdate<D, T> {
sets: Vec<(&'static str, ExprKind)>,
wheres: Vec<ExprKind>,
_marker: PhantomData<fn() -> (D, T)>,
}
impl<D, T> From<Assignments<T>> for ConflictUpdate<D, T> {
fn from(sets: Assignments<T>) -> Self {
ConflictUpdate {
sets: sets.into_sets(),
wheres: Vec::new(),
_marker: PhantomData,
}
}
}
impl<D, T: Table> ConflictUpdate<D, T> {
pub fn set_to<C, V, Idxs>(_column: Column<C>, value: V) -> Self
where
C: ColumnKey<Table = T> + Writable,
V: IntoExpr,
V::Sql: AssignsTo<C::Sql>,
ConflictScope<T>: Superset<V::Req, Idxs>,
{
ConflictUpdate {
sets: vec![(<C as crate::row::Named>::NAME, value.into_expr().kind)],
wheres: Vec::new(),
_marker: PhantomData,
}
}
pub fn and_set_to<C, V, Idxs>(mut self, _column: Column<C>, value: V) -> Self
where
C: ColumnKey<Table = T> + Writable,
V: IntoExpr,
V::Sql: AssignsTo<C::Sql>,
ConflictScope<T>: Superset<V::Req, Idxs>,
{
crate::update::push_set(
&mut self.sets,
<C as crate::row::Named>::NAME,
value.into_expr().kind,
);
self
}
pub fn filter<C: Condition<D, ConflictScope<T>, Idxs>, Idxs>(mut self, cond: C) -> Self {
self.wheres.push(cond.into_predicate().into_kind());
self
}
pub fn filter_all(
mut self,
conds: impl IntoIterator<Item = Predicate<D, ConflictScope<T>>>,
) -> Self {
self.wheres
.extend(conds.into_iter().map(Predicate::into_kind));
self
}
pub fn correlated<S, InnerSel>(
&self,
source: S,
selection: InnerSel,
) -> crate::select::Select<
D,
Cons<TableSlot<S::Table, NotNull>, ConflictScope<T>>,
InnerSel,
ConflictScope<T>,
>
where
S: crate::select::JoinSource<D>,
{
crate::select::correlated_with(source, selection)
}
}
impl<D: Dialect, T> ConflictUpdate<D, T> {
fn render_into(&self, sink: &mut dyn Sink) {
for (i, (col, value)) in self.sets.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
render_ident::<D>(sink, col);
sink.text(" = ");
crate::render::render_expr::<D>(value, sink);
}
crate::render::render_and_list::<D>(sink, " WHERE ", &self.wheres);
}
}
enum ConflictAction<D, T> {
DoNothing,
DoUpdate(ConflictUpdate<D, T>),
}
struct ConflictClause<D, T> {
target: Target,
action: ConflictAction<D, T>,
}
fn render_conflict_clause<D: Dialect, T>(clause: &ConflictClause<D, T>, sink: &mut dyn Sink) {
sink.text(" ON CONFLICT (");
for (i, c) in clause.target.columns.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
render_ident::<D>(sink, c);
}
sink.ch(')');
if let Some(predicate) = &clause.target.index_predicate {
sink.text(" WHERE ");
crate::render::render_expr::<D>(predicate, sink);
}
match &clause.action {
ConflictAction::DoNothing => sink.text(" DO NOTHING"),
ConflictAction::DoUpdate(sets) => {
sink.text(" DO UPDATE SET ");
sets.render_into(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 select<Scope, Sel, SelIdx, TgtIdx>(
self,
query: &crate::select::Select<D, Scope, Sel>,
) -> InsertSelect<D, T>
where
D: Dialect,
T: WrittenColumns,
T::Columns: crate::select::ColumnList<WrittenTable<T>, TgtIdx>,
TargetRow<T, TgtIdx>: crate::row::ColumnNames,
Sel: crate::select::Selection<Scope, SelIdx>,
Sel::Output: crate::row::SameShape<crate::row::Row<TargetRow<T, TgtIdx>>>,
{
InsertSelect {
header: <TargetRow<T, TgtIdx> as crate::row::ColumnNames>::names(),
body: query.fragment::<SelIdx>(),
_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,
})
}
}
#[doc(hidden)]
pub trait WrittenColumns {
type Columns;
}
type TargetRow<T, Idx> = <<T as WrittenColumns>::Columns as crate::select::ColumnList<
WrittenTable<T>,
Idx,
>>::Fields<crate::row::RowNil>;
pub struct InsertSelect<D, T> {
header: Vec<&'static str>,
body: crate::render::Fragment,
_marker: PhantomData<fn() -> (D, T)>,
}
impl<D: Dialect, T: Table> crate::statement::private::Sealed for InsertSelect<D, T> {}
impl<D: Dialect, T: Table> Statement for InsertSelect<D, T> {
type Dialect = D;
type Table = T;
fn render_into(&self, sink: &mut dyn Sink) {
sink.text("INSERT INTO ");
render_ident::<D>(sink, T::NAME);
sink.text(" (");
for (i, name) in self.header.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
render_ident::<D>(sink, name);
}
sink.text(") ");
self.body.splice_into(sink);
}
}
#[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>(
sink: &mut dyn Sink,
rows: &[Vec<InsertValue>],
on_conflict: &Option<ConflictClause<D, R::Table>>,
) {
sink.text("INSERT INTO ");
render_ident::<D>(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, sink);
}
return;
}
sink.text(" (");
for (i, name) in header.iter().enumerate() {
if i > 0 {
sink.text(", ");
}
render_ident::<D>(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, sink);
}
}
pub struct Insert<D, R: InsertRow> {
rows: Vec<Vec<InsertValue>>,
on_conflict: Option<ConflictClause<D, 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.into_target(),
action: ConflictAction::DoNothing,
});
self
}
pub fn on_conflict_do_update(
mut self,
target: impl ConflictTarget<R::Table>,
set: impl Into<ConflictUpdate<D, R::Table>>,
) -> Self
where
D: SupportsOnConflict,
{
self.on_conflict = Some(ConflictClause {
target: target.into_target(),
action: ConflictAction::DoUpdate(set.into()),
});
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_into(&self, sink: &mut dyn Sink) {
render_values_clause::<D, R>(sink, &self.rows, &self.on_conflict);
}
}