Skip to main content

drizzle_postgres/values/
update.rs

1//! Update value types for `PostgreSQL`.
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 super::PostgresValue;
7use super::insert::ValueWrapper;
8use crate::prelude::*;
9use drizzle_core::expr::Excluded;
10use drizzle_core::{
11    SQLColumnInfo, TypedPlaceholder, param::Param, placeholder::Placeholder, sql::SQL,
12    sql::SQLChunk, traits::SQLParam,
13};
14
15#[cfg(feature = "uuid")]
16use uuid::Uuid;
17
18/// Represents a value for UPDATE operations that can be skipped, null, or a SQL expression.
19#[derive(Debug, Clone, Default)]
20#[allow(clippy::large_enum_variant)]
21pub enum PostgresUpdateValue<'a, V: SQLParam, T> {
22    /// Don't include this column in the SET clause
23    #[default]
24    Skip,
25    /// Explicitly set column = NULL
26    Null,
27    /// Set column to a SQL expression (value, placeholder, etc.)
28    Value(ValueWrapper<'a, V, T>),
29}
30
31impl<V: SQLParam, T> PostgresUpdateValue<'_, V, T> {
32    /// Returns true if this is `Skip`
33    pub const fn is_skip(&self) -> bool {
34        matches!(self, Self::Skip)
35    }
36}
37
38// Generic conversion from any type T to UpdateValue
39impl<'a, T> From<T> for PostgresUpdateValue<'a, PostgresValue<'a>, T>
40where
41    T: TryInto<PostgresValue<'a>>,
42{
43    fn from(value: T) -> Self {
44        let sql = value.try_into().map_or_else(
45            |_| SQL::from(PostgresValue::Null),
46            |v: PostgresValue<'a>| SQL::from(v),
47        );
48        PostgresUpdateValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(sql))
49    }
50}
51
52// Specific conversion for &str to String UpdateValue
53impl<'a> From<&str> for PostgresUpdateValue<'a, PostgresValue<'a>, String> {
54    fn from(value: &str) -> Self {
55        let postgres_value = SQL::param(Cow::Owned(PostgresValue::from(value.to_string())));
56        PostgresUpdateValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(
57            postgres_value,
58        ))
59    }
60}
61
62// Placeholder conversion
63impl<'a, T> From<Placeholder> for PostgresUpdateValue<'a, PostgresValue<'a>, T> {
64    fn from(placeholder: Placeholder) -> Self {
65        let chunk = SQLChunk::Param(Param {
66            placeholder,
67            value: None,
68        });
69        PostgresUpdateValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(
70            core::iter::once(chunk).collect(),
71        ))
72    }
73}
74
75impl<'a, M: drizzle_core::types::DataType, N: drizzle_core::expr::Nullability, T>
76    From<TypedPlaceholder<M, N>> for PostgresUpdateValue<'a, PostgresValue<'a>, T>
77{
78    fn from(typed: TypedPlaceholder<M, N>) -> Self {
79        Placeholder::from(typed).into()
80    }
81}
82
83// Excluded column reference conversion (for ON CONFLICT DO UPDATE SET)
84impl<'a, C, T> From<Excluded<C>> for PostgresUpdateValue<'a, PostgresValue<'a>, T>
85where
86    C: SQLColumnInfo,
87{
88    fn from(excluded: Excluded<C>) -> Self {
89        use drizzle_core::ToSQL;
90        let sql = excluded.to_sql();
91        PostgresUpdateValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(sql))
92    }
93}
94
95// UUID conversion for String UpdateValue (for text columns)
96#[cfg(feature = "uuid")]
97impl<'a> From<Uuid> for PostgresUpdateValue<'a, PostgresValue<'a>, String> {
98    fn from(value: Uuid) -> Self {
99        let postgres_value = PostgresValue::Uuid(value);
100        let sql = SQL::param(postgres_value);
101        PostgresUpdateValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(sql))
102    }
103}
104
105#[cfg(feature = "uuid")]
106impl<'a> From<&'a Uuid> for PostgresUpdateValue<'a, PostgresValue<'a>, String> {
107    fn from(value: &'a Uuid) -> Self {
108        let postgres_value = PostgresValue::Uuid(*value);
109        let sql = SQL::param(postgres_value);
110        PostgresUpdateValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(sql))
111    }
112}