Skip to main content

drizzle_sqlite/values/
update.rs

1//! Update value types for `SQLite`.
2//!
3//! Each field in an UPDATE operation can be skipped (left unchanged),
4//! set to NULL, or set to a value or expression.
5
6use crate::prelude::*;
7use crate::types::Any;
8use drizzle_core::expr::{
9    AcceptsNullability, ColumnBinOp, ColumnNeg, Excluded, Expr, Null, Nullability, SQLExpr, Scalar,
10};
11use drizzle_core::types::{Assignable, DataType};
12use drizzle_core::{
13    Placeholder, SQL, SQLColumnInfo, SQLParam, SQLiteDialect, ToSQL, TypedPlaceholder,
14};
15
16use super::SQLiteValue;
17use super::insert::ValueWrapper;
18
19/// One field of a generated update model: unchanged, `NULL`, or a new value.
20///
21/// You rarely build this directly. The `with_*` setters on a generated
22/// `UpdateX` model convert their arguments into it.
23#[derive(Debug, Clone, Default)]
24#[allow(clippy::large_enum_variant)]
25pub enum SQLiteUpdateValue<
26    'a,
27    V: SQLParam,
28    T,
29    Target: DataType = Any,
30    TargetNull: Nullability = Null,
31> {
32    /// Leave the column out of the SET clause.
33    #[default]
34    Skip,
35    /// Set the column to `NULL`.
36    Null,
37    /// Set the column to a bound value, placeholder or SQL expression.
38    Value(ValueWrapper<'a, V, (T, Target, TargetNull)>),
39}
40
41impl<V: SQLParam, T, Target: DataType, TargetNull: Nullability>
42    SQLiteUpdateValue<'_, V, T, Target, TargetNull>
43{
44    /// Returns `true` if the column is left unchanged.
45    pub const fn is_skip(&self) -> bool {
46        matches!(self, Self::Skip)
47    }
48}
49
50/// Converts a setter argument to the column's type, then to a bound value.
51///
52/// # Panics
53///
54/// Panics when the argument does not fit the column type (an integer out of
55/// range, a JSON payload that fails to serialize), rather than storing NULL in
56/// its place.
57impl<'a, T, U, Target, TargetNull> From<T>
58    for SQLiteUpdateValue<'a, SQLiteValue<'a>, U, Target, TargetNull>
59where
60    T: TryInto<SQLiteValue<'a>> + TryInto<U>,
61    U: TryInto<SQLiteValue<'a>>,
62    Target: DataType,
63    TargetNull: Nullability,
64{
65    fn from(value: T) -> Self {
66        // A value that does not fit the column type is a caller bug; storing
67        // NULL in its place would lose it silently.
68        let column_value = TryInto::<U>::try_into(value).unwrap_or_else(|_| {
69            panic!(
70                "a `{}` does not fit a `{}` column",
71                core::any::type_name::<T>(),
72                core::any::type_name::<U>()
73            )
74        });
75        let sql = SQL::from(
76            TryInto::<SQLiteValue<'a>>::try_into(column_value).unwrap_or_else(|_| {
77                panic!(
78                    "could not convert a `{}` to a SQLite value",
79                    core::any::type_name::<U>()
80                )
81            }),
82        );
83        SQLiteUpdateValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(sql))
84    }
85}
86
87// Placeholder conversion
88impl<'a, T, Target, TargetNull> From<Placeholder>
89    for SQLiteUpdateValue<'a, SQLiteValue<'a>, T, Target, TargetNull>
90where
91    Target: DataType,
92    TargetNull: Nullability,
93{
94    fn from(placeholder: Placeholder) -> Self {
95        use drizzle_core::{Param, SQLChunk};
96        let chunk = SQLChunk::Param(Param {
97            placeholder,
98            value: None,
99        });
100        SQLiteUpdateValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(
101            core::iter::once(chunk).collect(),
102        ))
103    }
104}
105
106impl<'a, M, N, T, Target, TargetNull> From<TypedPlaceholder<M, N>>
107    for SQLiteUpdateValue<'a, SQLiteValue<'a>, T, Target, TargetNull>
108where
109    M: DataType,
110    N: Nullability,
111    Target: DataType + Assignable<M>,
112    TargetNull: Nullability + AcceptsNullability<N>,
113{
114    fn from(typed: TypedPlaceholder<M, N>) -> Self {
115        Placeholder::from(typed).into()
116    }
117}
118
119// Excluded column reference conversion (for ON CONFLICT DO UPDATE SET)
120impl<'a, C, T, Target, TargetNull, Actual, ActualNull> From<Excluded<C>>
121    for SQLiteUpdateValue<'a, SQLiteValue<'a>, T, Target, TargetNull>
122where
123    C: SQLColumnInfo + Expr<'a, SQLiteValue<'a>, SQLType = Actual, Nullable = ActualNull>,
124    Target: DataType + Assignable<Actual>,
125    TargetNull: Nullability + AcceptsNullability<ActualNull>,
126    Actual: DataType,
127    ActualNull: Nullability,
128{
129    fn from(excluded: Excluded<C>) -> Self {
130        use drizzle_core::ToSQL;
131        let sql = excluded.to_sql();
132        SQLiteUpdateValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(sql))
133    }
134}
135
136// Array conversion for Vec<u8> UpdateValue
137impl<'a, const N: usize, Target, TargetNull> From<[u8; N]>
138    for SQLiteUpdateValue<'a, SQLiteValue<'a>, Vec<u8>, Target, TargetNull>
139where
140    Target: DataType,
141    TargetNull: Nullability,
142{
143    fn from(value: [u8; N]) -> Self {
144        let sqlite_value = SQLiteValue::Blob(crate::prelude::Cow::Owned(value.to_vec()));
145        let sql = SQL::param(sqlite_value);
146        SQLiteUpdateValue::Value(ValueWrapper::<SQLiteValue<'a>, Vec<u8>>::new(sql))
147    }
148}
149
150impl<'a, T, Target, TargetNull, Actual, ActualNull, Sources>
151    From<SQLExpr<'a, SQLiteValue<'a>, Actual, ActualNull, Scalar, Sources>>
152    for SQLiteUpdateValue<'a, SQLiteValue<'a>, T, Target, TargetNull>
153where
154    Target: DataType + Assignable<Actual>,
155    TargetNull: Nullability + AcceptsNullability<ActualNull>,
156    Actual: DataType,
157    ActualNull: Nullability,
158{
159    fn from(value: SQLExpr<'a, SQLiteValue<'a>, Actual, ActualNull, Scalar, Sources>) -> Self {
160        Self::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(
161            value.into_expr_sql(),
162        ))
163    }
164}
165
166impl<'a, T, Target, TargetNull, L, R, Op, Actual, ActualNull>
167    From<ColumnBinOp<L, R, Op, SQLiteDialect, Actual, ActualNull>>
168    for SQLiteUpdateValue<'a, SQLiteValue<'a>, T, Target, TargetNull>
169where
170    Target: DataType + Assignable<Actual>,
171    TargetNull: Nullability + AcceptsNullability<ActualNull>,
172    Actual: DataType,
173    ActualNull: Nullability,
174    ColumnBinOp<L, R, Op, SQLiteDialect, Actual, ActualNull>: ToSQL<'a, SQLiteValue<'a>>,
175{
176    fn from(value: ColumnBinOp<L, R, Op, SQLiteDialect, Actual, ActualNull>) -> Self {
177        Self::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(value.into_sql()))
178    }
179}
180
181impl<'a, T, Target, TargetNull, E, Actual, ActualNull>
182    From<ColumnNeg<E, SQLiteDialect, Actual, ActualNull>>
183    for SQLiteUpdateValue<'a, SQLiteValue<'a>, T, Target, TargetNull>
184where
185    Target: DataType + Assignable<Actual>,
186    TargetNull: Nullability + AcceptsNullability<ActualNull>,
187    Actual: DataType,
188    ActualNull: Nullability,
189    ColumnNeg<E, SQLiteDialect, Actual, ActualNull>: ToSQL<'a, SQLiteValue<'a>>,
190{
191    fn from(value: ColumnNeg<E, SQLiteDialect, Actual, ActualNull>) -> Self {
192        Self::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(value.into_sql()))
193    }
194}