Skip to main content

drizzle_sqlite/values/
insert.rs

1//------------------------------------------------------------------------------
2// InsertValue Definition - SQL-based value for inserts
3//------------------------------------------------------------------------------
4
5use core::marker::PhantomData;
6
7use crate::prelude::*;
8use drizzle_core::{Placeholder, SQL, SQLParam, TypedPlaceholder};
9
10use super::{OwnedSQLiteValue, SQLiteValue};
11
12#[doc(hidden)]
13#[derive(Debug, Clone)]
14pub struct ValueWrapper<'a, V: SQLParam, T> {
15    pub value: SQL<'a, V>,
16    pub _phantom: PhantomData<T>,
17}
18
19impl<'a, V: SQLParam, T> ValueWrapper<'a, V, T> {
20    pub const fn new<U>(value: SQL<'a, V>) -> ValueWrapper<'a, V, U> {
21        ValueWrapper {
22            value,
23            _phantom: PhantomData,
24        }
25    }
26}
27
28/// Represents a value for INSERT operations that can be omitted, null, or a SQL expression
29#[derive(Debug, Clone, Default)]
30#[allow(clippy::large_enum_variant)]
31pub enum SQLiteInsertValue<'a, V: SQLParam, T> {
32    /// Omit this column from the INSERT (use database default)
33    #[default]
34    Omit,
35    /// Explicitly insert NULL
36    Null,
37    /// Insert a SQL expression (value, placeholder, etc.)
38    Value(ValueWrapper<'a, V, T>),
39}
40
41impl<'a, T> SQLiteInsertValue<'a, SQLiteValue<'a>, T> {
42    /// Converts this `InsertValue` to an owned version with 'static lifetime.
43    ///
44    /// The whole SQL fragment is kept: placeholders stay unbound and
45    /// expressions such as `json(?)` keep their shape, with every bound value
46    /// detached from its borrow.
47    #[must_use]
48    pub fn into_owned(self) -> SQLiteInsertValue<'static, SQLiteValue<'static>, T> {
49        match self {
50            SQLiteInsertValue::Omit => SQLiteInsertValue::Omit,
51            SQLiteInsertValue::Null => SQLiteInsertValue::Null,
52            SQLiteInsertValue::Value(wrapper) => {
53                let static_sql = wrapper
54                    .value
55                    .into_owned_with(|value| SQLiteValue::from(OwnedSQLiteValue::from(value)));
56                SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'static>, T>::new(static_sql))
57            }
58        }
59    }
60}
61
62/// Converts a setter argument to the column's type, then to a bound value.
63///
64/// # Panics
65///
66/// Panics when the argument does not fit the column type (an integer out of
67/// range, a JSON payload that fails to serialize), rather than storing NULL in
68/// its place.
69impl<'a, T, U> From<T> for SQLiteInsertValue<'a, SQLiteValue<'a>, U>
70where
71    T: TryInto<SQLiteValue<'a>> + TryInto<U>,
72    U: TryInto<SQLiteValue<'a>>,
73{
74    fn from(value: T) -> Self {
75        // A value that does not fit the column type is a caller bug; storing
76        // NULL in its place would lose it silently.
77        let column_value = TryInto::<U>::try_into(value).unwrap_or_else(|_| {
78            panic!(
79                "a `{}` does not fit a `{}` column",
80                core::any::type_name::<T>(),
81                core::any::type_name::<U>()
82            )
83        });
84        let sql = SQL::from(
85            TryInto::<SQLiteValue<'a>>::try_into(column_value).unwrap_or_else(|_| {
86                panic!(
87                    "could not convert a `{}` to a SQLite value",
88                    core::any::type_name::<U>()
89                )
90            }),
91        );
92        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(sql))
93    }
94}
95
96impl<'a, T> From<Placeholder> for SQLiteInsertValue<'a, SQLiteValue<'a>, T> {
97    fn from(placeholder: Placeholder) -> Self {
98        use drizzle_core::{Param, SQLChunk};
99        let chunk = SQLChunk::Param(Param {
100            placeholder,
101            value: None,
102        });
103        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(
104            core::iter::once(chunk).collect(),
105        ))
106    }
107}
108
109impl<'a, M: drizzle_core::types::DataType, N: drizzle_core::expr::Nullability, T>
110    From<TypedPlaceholder<M, N>> for SQLiteInsertValue<'a, SQLiteValue<'a>, T>
111{
112    fn from(typed: TypedPlaceholder<M, N>) -> Self {
113        Placeholder::from(typed).into()
114    }
115}
116
117// Array conversion for Vec<u8> InsertValue - support flexible input types
118impl<'a, const N: usize> From<[u8; N]> for SQLiteInsertValue<'a, SQLiteValue<'a>, Vec<u8>> {
119    fn from(value: [u8; N]) -> Self {
120        let sqlite_value = SQLiteValue::Blob(Cow::Owned(value.to_vec()));
121        let sql = SQL::param(sqlite_value);
122        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, Vec<u8>>::new(sql))
123    }
124}