drizzle_postgres/values/
insert.rs

1//! Insert value types for PostgreSQL
2
3use super::{OwnedPostgresValue, PostgresValue};
4use drizzle_core::{
5    ToSQL, param::Param, placeholder::Placeholder, sql::SQL, sql::SQLChunk, traits::SQLParam,
6};
7use std::borrow::Cow;
8use std::marker::PhantomData;
9
10#[cfg(feature = "uuid")]
11use uuid::Uuid;
12
13//------------------------------------------------------------------------------
14// InsertValue Definition - SQL-based value for inserts
15//------------------------------------------------------------------------------
16
17/// Wrapper for SQL with type information
18#[derive(Debug, Clone)]
19pub struct ValueWrapper<'a, V: SQLParam, T> {
20    pub value: SQL<'a, V>,
21    pub _phantom: PhantomData<T>,
22}
23
24impl<'a, V: SQLParam, T> ValueWrapper<'a, V, T> {
25    pub const fn new<U>(value: SQL<'a, V>) -> ValueWrapper<'a, V, U> {
26        ValueWrapper {
27            value,
28            _phantom: PhantomData,
29        }
30    }
31}
32
33/// Represents a value for INSERT operations that can be omitted, null, or a SQL expression
34#[derive(Debug, Clone, Default)]
35#[allow(clippy::large_enum_variant)]
36pub enum PostgresInsertValue<'a, V: SQLParam, T> {
37    /// Omit this column from the INSERT (use database default)
38    #[default]
39    Omit,
40    /// Explicitly insert NULL
41    Null,
42    /// Insert a SQL expression (value, placeholder, etc.)
43    Value(ValueWrapper<'a, V, T>),
44}
45
46impl<'a, T> PostgresInsertValue<'a, PostgresValue<'a>, T> {
47    /// Converts this InsertValue to an owned version with 'static lifetime
48    pub fn into_owned(self) -> PostgresInsertValue<'static, PostgresValue<'static>, T> {
49        match self {
50            PostgresInsertValue::Omit => PostgresInsertValue::Omit,
51            PostgresInsertValue::Null => PostgresInsertValue::Null,
52            PostgresInsertValue::Value(wrapper) => {
53                // Convert PostgresValue parameters to owned values
54                if let Some(SQLChunk::Param(param)) = wrapper.value.chunks.first() {
55                    if let Some(ref postgres_val) = param.value {
56                        let postgres_val = postgres_val.as_ref();
57                        let owned_postgres_val = OwnedPostgresValue::from(postgres_val.clone());
58                        let static_postgres_val = PostgresValue::from(owned_postgres_val);
59                        let static_sql = SQL::param(static_postgres_val);
60                        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'static>, T>::new(
61                            static_sql,
62                        ))
63                    } else {
64                        // NULL parameter
65                        let static_sql = SQL::param(PostgresValue::Null);
66                        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'static>, T>::new(
67                            static_sql,
68                        ))
69                    }
70                } else {
71                    // Non-parameter chunk, convert to NULL for simplicity
72                    let static_sql = SQL::param(PostgresValue::Null);
73                    PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'static>, T>::new(
74                        static_sql,
75                    ))
76                }
77            }
78        }
79    }
80}
81
82// Conversion implementations for PostgresValue-based InsertValue
83
84// Generic conversion from any type T to InsertValue (for same type T)
85// This works for types that implement TryInto<PostgresValue>, like enums,
86// ArrayString, ArrayVec, etc.
87impl<'a, T> From<T> for PostgresInsertValue<'a, PostgresValue<'a>, T>
88where
89    T: TryInto<PostgresValue<'a>>,
90{
91    fn from(value: T) -> Self {
92        let sql = value
93            .try_into()
94            .map(|v: PostgresValue<'a>| SQL::from(v))
95            .unwrap_or_else(|_| SQL::from(PostgresValue::Null));
96        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(sql))
97    }
98}
99
100// Specific conversion for &str to String InsertValue
101impl<'a> From<&str> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
102    fn from(value: &str) -> Self {
103        let postgres_value = SQL::param(Cow::Owned(PostgresValue::from(value.to_string())));
104        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(
105            postgres_value,
106        ))
107    }
108}
109
110// Placeholder conversion
111impl<'a, T> From<Placeholder> for PostgresInsertValue<'a, PostgresValue<'a>, T> {
112    fn from(placeholder: Placeholder) -> Self {
113        let chunk = SQLChunk::Param(Param {
114            placeholder,
115            value: None,
116        });
117        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(
118            std::iter::once(chunk).collect(),
119        ))
120    }
121}
122
123// Option conversion
124impl<'a, T> From<Option<T>> for PostgresInsertValue<'a, PostgresValue<'a>, T>
125where
126    T: ToSQL<'a, PostgresValue<'a>>,
127{
128    fn from(value: Option<T>) -> Self {
129        match value {
130            Some(v) => {
131                let sql = v.to_sql();
132                PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(sql))
133            }
134            None => PostgresInsertValue::Omit,
135        }
136    }
137}
138
139// UUID conversion for String InsertValue (for text columns)
140#[cfg(feature = "uuid")]
141impl<'a> From<Uuid> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
142    fn from(value: Uuid) -> Self {
143        let postgres_value = PostgresValue::Uuid(value);
144        let sql = SQL::param(postgres_value);
145        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(sql))
146    }
147}
148
149#[cfg(feature = "uuid")]
150impl<'a> From<&'a Uuid> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
151    fn from(value: &'a Uuid) -> Self {
152        let postgres_value = PostgresValue::Uuid(*value);
153        let sql = SQL::param(postgres_value);
154        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(sql))
155    }
156}