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