Skip to main content

qbrs_core/
insert.rs

1//! `INSERT INTO .. VALUES ..`.
2//!
3//! A column with a schema default gets a `Defaultable<T>` field, so "omit"
4//! and "explicit value" stay distinguishable — and `Defaultable<Option<T>>`
5//! when it's also nullable, making that three distinct states. Omission
6//! renders as the `DEFAULT` keyword in that row's `VALUES (..)` tuple rather
7//! than changing the column list, so rows that omit different fields still
8//! share one statement.
9
10use std::marker::PhantomData;
11
12use crate::dialect::{Dialect, SupportsOnConflict};
13use crate::expr::{Column, ColumnKey, Value};
14use crate::render::{QuerySink, Sink, render_ident};
15use crate::scope::{BaseTable, Table};
16use crate::statement::Statement;
17use crate::update::Assignments;
18
19/// What a column's setter accepts, keyed by what that column's field
20/// holds: the column's own Rust type — `&str` for text — plus the `Option`
21/// a request struct already carries, wherever leaving the column out means
22/// something. What `None` means is the position's own: on an insert it is
23/// NULL for a nullable column and the schema's default for a defaulted one
24/// (`.<column>_null()` says the other, where a column is both); on an
25/// update it is *untouched*, since an `UPDATE` that says nothing about a
26/// column leaves it alone.
27#[diagnostic::on_unimplemented(
28    message = "`{Self}` isn't a value this column accepts",
29    label = "expected the column's own Rust type, or an `Option` of it"
30)]
31pub trait IntoColumnValue<V> {
32    fn into_column_value(self) -> V;
33}
34
35mod insertable {
36    /// Sealed like the other markers a derive emits. Unlike `Filled`, whose
37    /// doc explains why forging it buys nothing, forging this one turns a
38    /// bulk insert into a single `DEFAULT VALUES` — rows lost quietly — so
39    /// it gets the door even though a determined caller can still write it.
40    pub trait Sealed {}
41}
42
43#[doc(hidden)]
44pub use insertable::Sealed as InsertableSealed;
45
46/// A table with at least one column a statement may insert into. Emitted by
47/// `#[derive(Table)]` unless every column is generated, which leaves an
48/// `INSERT` with nothing to name: SQL spells that `DEFAULT VALUES`, and
49/// spells it for exactly one row.
50#[diagnostic::on_unimplemented(
51    message = "every column of `{Self}`'s table is generated, so only one row at a time can be inserted",
52    label = "`DEFAULT VALUES` is what SQL calls a row with nothing in it, and it names no columns to repeat",
53    note = "insert them one statement at a time"
54)]
55pub trait Insertable: InsertableSealed {}
56
57/// Proof that a builder's slot for column `C` holds that column's value.
58/// Deliberately unsealed, unlike `scope::Find`: forging it buys nothing,
59/// because `*Insert`'s fields are public and a complete row with a value of
60/// the caller's choosing is directly constructible. What the type-state
61/// builder prevents is *forgetting* a column, not choosing its value — and
62/// a seal here can't hold anyway, since the derive must implement this in
63/// the schema's own crate, where any nameable proof is nameable twice.
64/// `Missing<C>` doesn't implement it, which is what `build()` is bounded
65/// by — on the method rather than by the slot's type, so an incomplete row
66/// is a sentence naming the column rather than a missing `build`.
67#[diagnostic::on_unimplemented(
68    message = "column `{C}` hasn't been given a value yet",
69    label = "every column that is neither nullable nor defaulted needs one before `.build()`"
70)]
71pub trait Filled<C> {
72    #[doc(hidden)]
73    type Value;
74    #[doc(hidden)]
75    fn filled(self) -> Self::Value;
76}
77
78/// A column an `*Insert` builder hasn't been given a value for yet. Named
79/// after the column so the builder's type says which one is missing, rather
80/// than leaving a bare `()` to be counted by position.
81pub struct Missing<C>(std::marker::PhantomData<fn() -> C>);
82
83impl<C> Missing<C> {
84    #[doc(hidden)]
85    pub const fn new() -> Self {
86        Missing(std::marker::PhantomData)
87    }
88}
89
90impl<C> Default for Missing<C> {
91    fn default() -> Self {
92        Missing::new()
93    }
94}
95
96#[derive(Debug, Clone, PartialEq, Default)]
97pub enum Defaultable<T> {
98    #[default]
99    Default,
100    Value(T),
101}
102
103#[derive(Debug, Clone)]
104pub enum InsertValue {
105    Value(Value),
106    /// Renders as the bare `DEFAULT` keyword in the `VALUES` list.
107    Default,
108}
109
110impl<T: Into<Value>> From<Defaultable<T>> for InsertValue {
111    fn from(d: Defaultable<T>) -> Self {
112        match d {
113            Defaultable::Default => InsertValue::Default,
114            Defaultable::Value(v) => InsertValue::Value(v.into()),
115        }
116    }
117}
118
119/// Implemented by the `#[derive(Table)]`-generated `*Insert` struct for each
120/// table. One list of `(column, value)` pairs rather than a name list beside
121/// a value list, for the reason `select::AllColumns` carries one list: the
122/// seal is `#[doc(hidden)] pub` — the derive has to write it in the schema's
123/// own crate — so two lists that have to line up position for position
124/// could be made not to, and `INSERT INTO t (a, b, c) VALUES ($1)` is
125/// malformed whatever the table looks like. `update::UpdateRow::sets` has
126/// always had this shape.
127pub trait InsertRow: private::Sealed {
128    type Table: Table;
129
130    /// This row's columns *and* values as one chain: the keys spell the
131    /// statement's header, the cells carry what goes under them. One type,
132    /// because two lists reconciled at render time is the shape that made
133    /// every earlier version of this trait able to produce SQL malformed
134    /// for any table, or to drop a value silently — a `COLUMNS` const
135    /// beside positional values, then pairs matched against the first row's
136    /// names, then pairs matched against a declared header. A chain can
137    /// carry neither a surplus cell nor a missing one.
138    type Values: InsertValues;
139
140    fn into_values(self) -> Self::Values;
141}
142
143mod insert_values {
144    /// Sealed to the two shapes a chain has, so `Values` is always a real
145    /// one — the reason `row::ColumnNames` is sealed.
146    pub trait Sealed {}
147}
148
149/// One row's cells, in the order its chain declares them.
150fn collect_values<R: InsertRow>(row: R) -> Vec<InsertValue> {
151    let mut out = Vec::new();
152    row.into_values().push_values(&mut out);
153    out
154}
155
156/// A chain of `(column, value)` cells: `RowCons<C, InsertValue, Tail>` down
157/// to `RowNil`. Walked once for the header and once for each row's values,
158/// so the two cannot disagree.
159pub trait InsertValues: insert_values::Sealed {
160    #[doc(hidden)]
161    fn push_names(out: &mut Vec<&'static str>);
162    #[doc(hidden)]
163    fn push_values(self, out: &mut Vec<InsertValue>);
164}
165
166impl insert_values::Sealed for crate::row::RowNil {}
167
168impl InsertValues for crate::row::RowNil {
169    fn push_names(_out: &mut Vec<&'static str>) {}
170    fn push_values(self, _out: &mut Vec<InsertValue>) {}
171}
172
173impl<C: crate::row::Named, Tail: InsertValues> insert_values::Sealed
174    for crate::row::RowCons<C, InsertValue, Tail>
175{
176}
177
178impl<C: crate::row::Named, Tail: InsertValues> InsertValues
179    for crate::row::RowCons<C, InsertValue, Tail>
180{
181    fn push_names(out: &mut Vec<&'static str>) {
182        out.push(<C as crate::row::Named>::NAME);
183        Tail::push_names(out);
184    }
185
186    fn push_values(self, out: &mut Vec<InsertValue>) {
187        let (value, tail) = self.into_cell();
188        out.push(value);
189        tail.push_values(out);
190    }
191}
192
193/// An `ON CONFLICT` target: one or more columns proven by `T` to belong to
194/// the table being inserted into, where a raw `&[&str]` would let a typo
195/// through to the database. Implemented for a bare `Column<C>` and for
196/// tuples of up to three; add arities as real schemas need them.
197#[diagnostic::on_unimplemented(
198    message = "`{Self}` isn't an `ON CONFLICT` target for `{T}`",
199    label = "a column of that table, or a tuple of up to three of them"
200)]
201pub trait ConflictTarget<T: Table>: conflict_target::Sealed {
202    #[doc(hidden)]
203    fn column_names(&self) -> Vec<&'static str>;
204}
205
206mod conflict_target {
207    /// Sealed for the reason `InsertRow` is: a hand-written impl could name
208    /// a column that isn't there, and the point of taking `Column<C>`s is
209    /// that it can't.
210    pub trait Sealed {}
211    impl<C: crate::expr::ColumnKey> Sealed for crate::expr::Column<C> {}
212    // Elements constrained, or the seal admits a tuple of anything — and a
213    // hand-written `ConflictTarget` for it could name a column that isn't
214    // there, which is what this seal is for.
215    impl<A: crate::expr::ColumnKey> Sealed for (crate::expr::Column<A>,) {}
216    impl<A: crate::expr::ColumnKey, B: crate::expr::ColumnKey> Sealed
217        for (crate::expr::Column<A>, crate::expr::Column<B>)
218    {
219    }
220    impl<A: crate::expr::ColumnKey, B: crate::expr::ColumnKey, C: crate::expr::ColumnKey> Sealed
221        for (
222            crate::expr::Column<A>,
223            crate::expr::Column<B>,
224            crate::expr::Column<C>,
225        )
226    {
227    }
228}
229
230impl<C: ColumnKey> ConflictTarget<C::Table> for Column<C> {
231    fn column_names(&self) -> Vec<&'static str> {
232        vec![C::NAME]
233    }
234}
235
236macro_rules! conflict_target_tuple {
237    ($($name:ident),+) => {
238        #[allow(non_snake_case)]
239        impl<T: Table, $($name: ColumnKey<Table = T>,)+> ConflictTarget<T> for ($(Column<$name>,)+) {
240            fn column_names(&self) -> Vec<&'static str> {
241                vec![$(<$name as crate::row::Named>::NAME),+]
242            }
243        }
244    };
245}
246// Columns, not nested targets: a conflict target is a list of columns, and
247// letting it nest is what made the documented limit of three not one.
248conflict_target_tuple!(A);
249conflict_target_tuple!(A, B);
250conflict_target_tuple!(A, B, C);
251
252enum ConflictAction<T> {
253    DoNothing,
254    /// The same `SET` list `UPDATE` takes: an `*Update` value, or
255    /// `Assignments` of expressions.
256    ///
257    /// **Known limitation**: no `EXCLUDED.column` (`SET total = total +
258    /// EXCLUDED.total`) — the row being inserted isn't a table the scope
259    /// knows, so referring to it needs its own typed API. Everything else a
260    /// `SET` list can say, including expressions over the target's own
261    /// columns, works here.
262    DoUpdate(Assignments<T>),
263}
264
265struct ConflictClause<T> {
266    target: Vec<&'static str>,
267    action: ConflictAction<T>,
268}
269
270fn render_conflict_clause<D: Dialect, T>(clause: &ConflictClause<T>, sink: &mut dyn Sink) {
271    sink.text(" ON CONFLICT (");
272    for (i, c) in clause.target.iter().enumerate() {
273        if i > 0 {
274            sink.text(", ");
275        }
276        render_ident::<D>(sink, c);
277    }
278    sink.ch(')');
279    match &clause.action {
280        ConflictAction::DoNothing => sink.text(" DO NOTHING"),
281        ConflictAction::DoUpdate(sets) => {
282            sink.text(" DO UPDATE SET ");
283            sets.render_into::<D>(sink);
284        }
285    }
286}
287
288mod private {
289    /// The row's `(column, value)` pairs are what a statement writes;
290    /// `#[derive(Table)]` is what guarantees that, so it is the only thing
291    /// that can produce an `InsertRow`.
292    pub trait Sealed {}
293}
294
295#[doc(hidden)]
296pub use private::Sealed as InsertRowSealed;
297
298pub struct InsertSeed<D, T> {
299    _marker: PhantomData<fn() -> (D, T)>,
300}
301
302pub fn insert<D, T: BaseTable>(_table: T) -> InsertSeed<D, T> {
303    InsertSeed {
304        _marker: PhantomData,
305    }
306}
307
308impl<D, T: Table> InsertSeed<D, T> {
309    pub fn values<R: InsertRow<Table = T>>(self, row: R) -> Insert<D, R> {
310        Insert {
311            rows: vec![collect_values(row)],
312            on_conflict: None,
313            _marker: PhantomData,
314        }
315    }
316
317    /// Every row of a collection at once — the shape a bulk import has,
318    /// where the rows are already in a `Vec` and the first one isn't
319    /// special. `INSERT` with no rows has no SQL form, so an empty
320    /// collection is refused here rather than rendered.
321    pub fn values_all<R: InsertRow<Table = T> + Insertable>(
322        self,
323        rows: impl IntoIterator<Item = R>,
324    ) -> Result<Insert<D, R>, NothingToInsert> {
325        let rows: Vec<_> = rows.into_iter().map(collect_values).collect();
326        if rows.is_empty() {
327            return Err(NothingToInsert);
328        }
329        Ok(Insert {
330            rows,
331            on_conflict: None,
332            _marker: PhantomData,
333        })
334    }
335}
336
337/// An `INSERT` was given no rows at all. Returned rather than panicked for
338/// the reason `update::NothingToSet` gives: an empty collection is ordinary
339/// request-shaped data, and the caller decides whether it is a no-op or an
340/// error.
341#[derive(Debug, Clone, Copy, PartialEq, Eq)]
342pub struct NothingToInsert;
343
344impl std::fmt::Display for NothingToInsert {
345    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346        f.write_str("an INSERT must have at least one row, but no rows were given")
347    }
348}
349impl std::error::Error for NothingToInsert {}
350
351fn render_values_clause<D: Dialect, R: InsertRow>(
352    rows: &[Vec<InsertValue>],
353    on_conflict: &Option<ConflictClause<R::Table>>,
354) -> QuerySink<D> {
355    let mut sink = QuerySink::<D>::new();
356    sink.text("INSERT INTO ");
357    render_ident::<D>(&mut sink, <R::Table as Table>::NAME);
358
359    // Header and values come from one chain, so a row has exactly one cell
360    // per column named — no reconciliation, nothing to drop.
361    let mut header = Vec::new();
362    <R::Values as InsertValues>::push_names(&mut header);
363
364    // A table whose every column is generated leaves nothing to name, and
365    // an empty column list is a syntax error in two of the three dialects.
366    // One such row is all SQL can express, which is why `values`/`values_all`
367    // take `Insertable`.
368    if header.is_empty() {
369        sink.text(D::INSERT_NO_COLUMNS);
370        if let Some(clause) = on_conflict {
371            render_conflict_clause::<D, _>(clause, &mut sink);
372        }
373        return sink;
374    }
375
376    sink.text(" (");
377    for (i, name) in header.iter().enumerate() {
378        if i > 0 {
379            sink.text(", ");
380        }
381        render_ident::<D>(&mut sink, name);
382    }
383    sink.text(") VALUES ");
384
385    for (row_i, row) in rows.iter().enumerate() {
386        if row_i > 0 {
387            sink.text(", ");
388        }
389        sink.ch('(');
390        for (i, v) in row.iter().enumerate() {
391            if i > 0 {
392                sink.text(", ");
393            }
394            match v {
395                InsertValue::Default => sink.text("DEFAULT"),
396                InsertValue::Value(v) => sink.bind(v),
397            }
398        }
399        sink.ch(')');
400    }
401
402    if let Some(clause) = on_conflict {
403        render_conflict_clause::<D, _>(clause, &mut sink);
404    }
405
406    sink
407}
408
409pub struct Insert<D, R: InsertRow> {
410    rows: Vec<Vec<InsertValue>>,
411    on_conflict: Option<ConflictClause<R::Table>>,
412    _marker: PhantomData<fn() -> (D, R)>,
413}
414
415impl<D, R: InsertRow + Insertable> Insert<D, R> {
416    /// Bulk insert: add another row to the same statement.
417    pub fn values(mut self, row: R) -> Self {
418        self.rows.push(collect_values(row));
419        self
420    }
421
422    /// The same for a collection. Infallible, unlike the seed's: this
423    /// statement already has a row, so an empty collection adds nothing
424    /// rather than describing an `INSERT` with nothing in it.
425    pub fn values_all(mut self, rows: impl IntoIterator<Item = R>) -> Self {
426        self.rows.extend(rows.into_iter().map(collect_values));
427        self
428    }
429}
430
431impl<D: Dialect, R: InsertRow> Insert<D, R> {
432    /// `ON CONFLICT (..) DO NOTHING`.
433    pub fn on_conflict_do_nothing(mut self, target: impl ConflictTarget<R::Table>) -> Self
434    where
435        D: SupportsOnConflict,
436    {
437        self.on_conflict = Some(ConflictClause {
438            target: target.column_names(),
439            action: ConflictAction::DoNothing,
440        });
441        self
442    }
443
444    /// `ON CONFLICT (..) DO UPDATE SET ..`, taking the same `Assignments`
445    /// an `UPDATE` sets.
446    pub fn on_conflict_do_update(
447        mut self,
448        target: impl ConflictTarget<R::Table>,
449        set: Assignments<R::Table>,
450    ) -> Self
451    where
452        D: SupportsOnConflict,
453    {
454        self.on_conflict = Some(ConflictClause {
455            target: target.column_names(),
456            action: ConflictAction::DoUpdate(set),
457        });
458        self
459    }
460}
461
462impl<D: Dialect, R: InsertRow> crate::statement::private::Sealed for Insert<D, R> {}
463
464impl<D: Dialect, R: InsertRow> Statement for Insert<D, R> {
465    type Dialect = D;
466    type Table = R::Table;
467    fn render(&self) -> QuerySink<D> {
468        render_values_clause::<D, R>(&self.rows, &self.on_conflict)
469    }
470}