Skip to main content

drizzle_postgres/values/
insert.rs

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