sea_orm/entity/active_value.rs
1use crate::Value;
2use sea_query::Nullable;
3use std::fmt::Debug;
4
5pub use ActiveValue::{NotSet, Set, Unchanged};
6
7/// The state of a field in an [ActiveModel][ActiveModelTrait].
8///
9/// There are three possible states represented by three enum variants:
10///
11/// - [Set] - a value that's explicitly set by the application and sent to the database.
12/// - [Unchanged] - an existing, unchanged value from the database.
13/// - [NotSet] - an undefined value (nothing is sent to the database).
14///
15/// The difference between these states is useful
16/// when constructing `INSERT` and `UPDATE` SQL statements (see an example below).
17/// It's also useful for knowing which fields have changed in a record.
18///
19/// # Examples
20///
21/// ```
22/// use sea_orm::tests_cfg::{cake, fruit};
23/// use sea_orm::{DbBackend, entity::*, query::*};
24///
25/// // Here, we use `NotSet` to let the database automatically generate an `id`.
26/// // This is different from `Set(None)` that explicitly sets `cake_id` to `NULL`.
27/// assert_eq!(
28/// Insert::one(fruit::ActiveModel {
29/// id: ActiveValue::NotSet,
30/// name: ActiveValue::Set("Orange".to_owned()),
31/// cake_id: ActiveValue::Set(None),
32/// })
33/// .build(DbBackend::Postgres)
34/// .to_string(),
35/// r#"INSERT INTO "fruit" ("name", "cake_id") VALUES ('Orange', NULL)"#
36/// );
37///
38/// // Here, we update the record, set `cake_id` to the new value
39/// // and use `NotSet` to avoid updating the `name` field.
40/// // `id` is the primary key, so it's used in the condition and not updated.
41/// assert_eq!(
42/// Update::one(fruit::ActiveModel {
43/// id: ActiveValue::Unchanged(1),
44/// name: ActiveValue::NotSet,
45/// cake_id: ActiveValue::Set(Some(2)),
46/// })
47/// .validate()
48/// .unwrap()
49/// .build(DbBackend::Postgres)
50/// .to_string(),
51/// r#"UPDATE "fruit" SET "cake_id" = 2 WHERE "fruit"."id" = 1"#
52/// );
53/// ```
54#[derive(Clone, Debug)]
55pub enum ActiveValue<V>
56where
57 V: Into<Value>,
58{
59 /// A [Value] that's explicitly set by the application and sent to the database.
60 ///
61 /// Use this to insert or set a specific value.
62 ///
63 /// When editing an existing value, you can use [set_if_not_equals][ActiveValue::set_if_not_equals]
64 /// to preserve the [Unchanged] state when the new value is the same as the old one.
65 /// Then you can meaningfully use methods like [ActiveModelTrait::is_changed].
66 Set(V),
67 /// An existing, unchanged [Value] from the database.
68 ///
69 /// You get these when you query an existing [Model][crate::ModelTrait]
70 /// from the database and convert it into an [ActiveModel][ActiveModelTrait].
71 ///
72 /// When you edit it, you can use [set_if_not_equals][ActiveValue::set_if_not_equals]
73 /// to preserve this "unchanged" state if the new value is the same as the old one.
74 /// Then you can meaningfully use methods like [ActiveModelTrait::is_changed].
75 Unchanged(V),
76 /// An undefined [Value]. Nothing is sent to the database.
77 ///
78 /// When you create a new [ActiveModel][ActiveModelTrait],
79 /// its fields are [NotSet][ActiveValue::NotSet] by default.
80 ///
81 /// This can be useful when:
82 ///
83 /// - You insert a new record and want the database to generate a default value (e.g., an id).
84 /// - In an `UPDATE` statement, you don't want to update some field.
85 NotSet,
86}
87
88/// Defines an not set operation on an [ActiveValue]
89#[deprecated(
90 since = "0.5.0",
91 note = "Please use [`ActiveValue::NotSet`] or [`NotSet`]"
92)]
93#[allow(non_snake_case)]
94pub fn Unset<V>(_: Option<bool>) -> ActiveValue<V>
95where
96 V: Into<Value>,
97{
98 ActiveValue::not_set()
99}
100
101/// Any type that can be converted into an [ActiveValue]
102pub trait IntoActiveValue<V>
103where
104 V: Into<Value>,
105{
106 /// Method to perform the conversion
107 fn into_active_value(self) -> ActiveValue<V>;
108}
109
110impl<V> IntoActiveValue<V> for Option<V>
111where
112 V: IntoActiveValue<V> + Into<Value> + Nullable,
113{
114 fn into_active_value(self) -> ActiveValue<V> {
115 match self {
116 Some(value) => Set(value),
117 None => NotSet,
118 }
119 }
120}
121
122impl<V> IntoActiveValue<Option<V>> for Option<Option<V>>
123where
124 V: IntoActiveValue<V> + Into<Value> + Nullable,
125{
126 fn into_active_value(self) -> ActiveValue<Option<V>> {
127 match self {
128 Some(value) => Set(value),
129 None => NotSet,
130 }
131 }
132}
133
134macro_rules! impl_into_active_value {
135 ($ty: ty) => {
136 impl IntoActiveValue<$ty> for $ty {
137 fn into_active_value(self) -> ActiveValue<$ty> {
138 Set(self)
139 }
140 }
141 };
142}
143
144impl_into_active_value!(bool);
145impl_into_active_value!(i8);
146impl_into_active_value!(i16);
147impl_into_active_value!(i32);
148impl_into_active_value!(i64);
149impl_into_active_value!(u8);
150impl_into_active_value!(u16);
151impl_into_active_value!(u32);
152impl_into_active_value!(u64);
153impl_into_active_value!(f32);
154impl_into_active_value!(f64);
155impl_into_active_value!(&'static str);
156impl_into_active_value!(String);
157impl_into_active_value!(Vec<u8>);
158
159#[cfg(feature = "with-json")]
160#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
161impl_into_active_value!(crate::prelude::Json);
162
163#[cfg(feature = "with-chrono")]
164#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
165impl_into_active_value!(crate::prelude::Date);
166
167#[cfg(feature = "with-chrono")]
168#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
169impl_into_active_value!(crate::prelude::Time);
170
171#[cfg(feature = "with-chrono")]
172#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
173impl_into_active_value!(crate::prelude::DateTime);
174
175#[cfg(feature = "with-chrono")]
176#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
177impl_into_active_value!(crate::prelude::DateTimeWithTimeZone);
178
179#[cfg(feature = "with-chrono")]
180#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
181impl_into_active_value!(crate::prelude::DateTimeUtc);
182
183#[cfg(feature = "with-chrono")]
184#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
185impl_into_active_value!(crate::prelude::DateTimeLocal);
186
187#[cfg(feature = "with-rust_decimal")]
188#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
189impl_into_active_value!(crate::prelude::Decimal);
190
191#[cfg(feature = "with-bigdecimal")]
192#[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))]
193impl_into_active_value!(crate::prelude::BigDecimal);
194
195#[cfg(feature = "with-uuid")]
196#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
197impl_into_active_value!(crate::prelude::Uuid);
198
199#[cfg(feature = "with-time")]
200#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
201impl_into_active_value!(crate::prelude::TimeDate);
202
203#[cfg(feature = "with-time")]
204#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
205impl_into_active_value!(crate::prelude::TimeTime);
206
207#[cfg(feature = "with-time")]
208#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
209impl_into_active_value!(crate::prelude::TimeDateTime);
210
211#[cfg(feature = "with-time")]
212#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))]
213impl_into_active_value!(crate::prelude::TimeDateTimeWithTimeZone);
214
215#[cfg(feature = "with-ipnetwork")]
216#[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))]
217impl_into_active_value!(crate::prelude::IpNetwork);
218
219impl<V> Default for ActiveValue<V>
220where
221 V: Into<Value>,
222{
223 /// Create an [ActiveValue::NotSet]
224 fn default() -> Self {
225 Self::NotSet
226 }
227}
228
229impl<V> ActiveValue<V>
230where
231 V: Into<Value>,
232{
233 /// Create an [ActiveValue::Set]
234 pub fn set(value: V) -> Self {
235 Self::Set(value)
236 }
237
238 /// Check if the [ActiveValue] is [ActiveValue::Set]
239 pub fn is_set(&self) -> bool {
240 matches!(self, Self::Set(_))
241 }
242
243 /// Create an [ActiveValue::Unchanged]
244 pub fn unchanged(value: V) -> Self {
245 Self::Unchanged(value)
246 }
247
248 /// Check if the [ActiveValue] is [ActiveValue::Unchanged]
249 pub fn is_unchanged(&self) -> bool {
250 matches!(self, Self::Unchanged(_))
251 }
252
253 /// Create an [ActiveValue::NotSet]
254 pub fn not_set() -> Self {
255 Self::default()
256 }
257
258 /// Check if the [ActiveValue] is [ActiveValue::NotSet]
259 pub fn is_not_set(&self) -> bool {
260 matches!(self, Self::NotSet)
261 }
262
263 /// Take ownership of the inner value, also setting self to `NotSet`
264 pub fn take(&mut self) -> Option<V> {
265 match std::mem::take(self) {
266 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
267 ActiveValue::NotSet => None,
268 }
269 }
270
271 /// Get an owned value of the [ActiveValue]
272 ///
273 /// # Panics
274 ///
275 /// Panics if it is [ActiveValue::NotSet]
276 pub fn unwrap(self) -> V {
277 match self {
278 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
279 ActiveValue::NotSet => panic!("Cannot unwrap ActiveValue::NotSet"),
280 }
281 }
282
283 /// Take ownership of the inner value, consuming self
284 pub fn into_value(self) -> Option<Value> {
285 match self {
286 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value.into()),
287 ActiveValue::NotSet => None,
288 }
289 }
290
291 /// Wrap the [Value] into a `ActiveValue<Value>`
292 pub fn into_wrapped_value(self) -> ActiveValue<Value> {
293 match self {
294 Self::Set(value) => ActiveValue::set(value.into()),
295 Self::Unchanged(value) => ActiveValue::unchanged(value.into()),
296 Self::NotSet => ActiveValue::not_set(),
297 }
298 }
299
300 /// Reset the value from [ActiveValue::Unchanged] to [ActiveValue::Set],
301 /// leaving [ActiveValue::NotSet] untouched.
302 pub fn reset(&mut self) {
303 *self = match self.take() {
304 Some(value) => ActiveValue::Set(value),
305 None => ActiveValue::NotSet,
306 };
307 }
308
309 /// `Set(value)`, except when [`self.is_unchanged()`][ActiveValue#method.is_unchanged]
310 /// and `value` equals the current [Unchanged][ActiveValue::Unchanged] value.
311 ///
312 /// This is useful when you have an [Unchanged][ActiveValue::Unchanged] value from the database,
313 /// then update it using this method,
314 /// and then use [`.is_unchanged()`][ActiveValue#method.is_unchanged] to see whether it has *actually* changed.
315 ///
316 /// The same nice effect applies to the entire `ActiveModel`.
317 /// You can now meaningfully use [ActiveModelTrait::is_changed][ActiveModelTrait#method.is_changed]
318 /// to see whether are any changes that need to be saved to the database.
319 ///
320 /// ## Examples
321 ///
322 /// ```
323 /// # use sea_orm::ActiveValue;
324 /// #
325 /// let mut value = ActiveValue::Unchanged("old");
326 ///
327 /// // This wouldn't be the case if we used plain `value = Set("old");`
328 /// value.set_if_not_equals("old");
329 /// assert!(value.is_unchanged());
330 ///
331 /// // Only when we change the actual `&str` value, it becomes `Set`
332 /// value.set_if_not_equals("new");
333 /// assert_eq!(value.is_unchanged(), false);
334 /// assert_eq!(value, ActiveValue::Set("new"));
335 /// ```
336 pub fn set_if_not_equals(&mut self, value: V)
337 where
338 V: PartialEq,
339 {
340 match self {
341 ActiveValue::Unchanged(current) if &value == current => {}
342 _ => *self = ActiveValue::Set(value),
343 }
344 }
345
346 /// `Set(value)`, except when [`self.is_unchanged()`][ActiveValue#method.is_unchanged],
347 /// `value` equals the current [Unchanged][ActiveValue::Unchanged] value, and `value`
348 /// does not match a given predicate.
349 ///
350 /// This is useful in the same situations as [ActiveValue#method.set_if_not_equals] as
351 /// well as when you want to leave an existing [Set][ActiveValue::Set] value alone
352 /// depending on a condition, such as ensuring a `None` value never replaced an
353 /// existing `Some` value. This can come up when trying to merge two [ActiveValue]s.
354 ///
355 /// ## Examples
356 ///
357 /// ```
358 /// # use sea_orm::ActiveValue;
359 /// #
360 /// let mut value = ActiveValue::Set(Some("old"));
361 ///
362 /// // since Option::is_some(None) == false, we leave the existing set value alone
363 /// value.set_if_not_equals_and(None, Option::is_some);
364 /// assert_eq!(value, ActiveValue::Set(Some("old")));
365 ///
366 /// // since Option::is_some(Some("new")) == true, we replace the set value
367 /// value.set_if_not_equals_and(Some("new"), Option::is_some);
368 /// assert_eq!(value, ActiveValue::Set(Some("new")));
369 /// ```
370 pub fn set_if_not_equals_and(&mut self, value: V, f: impl FnOnce(&V) -> bool)
371 where
372 V: PartialEq,
373 {
374 match self {
375 ActiveValue::Unchanged(current) if &value == current => {}
376 ActiveValue::Set(_) if !f(&value) => {}
377 _ => *self = ActiveValue::Set(value),
378 }
379 }
380
381 /// Get the inner value, unless `self` is [NotSet][ActiveValue::NotSet].
382 ///
383 /// There's also a panicking version: [ActiveValue::as_ref].
384 ///
385 /// ## Examples
386 ///
387 /// ```
388 /// # use sea_orm::ActiveValue;
389 /// #
390 /// assert_eq!(ActiveValue::Unchanged(42).try_as_ref(), Some(&42));
391 /// assert_eq!(ActiveValue::Set(42).try_as_ref(), Some(&42));
392 /// assert_eq!(ActiveValue::NotSet.try_as_ref(), None::<&i32>);
393 /// ```
394 pub fn try_as_ref(&self) -> Option<&V> {
395 match self {
396 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => Some(value),
397 ActiveValue::NotSet => None,
398 }
399 }
400}
401
402impl<V> std::convert::AsRef<V> for ActiveValue<V>
403where
404 V: Into<Value>,
405{
406 /// # Panics
407 ///
408 /// Panics if it is [ActiveValue::NotSet].
409 ///
410 /// See [ActiveValue::try_as_ref] for a fallible non-panicking version.
411 fn as_ref(&self) -> &V {
412 match self {
413 ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value,
414 ActiveValue::NotSet => panic!("Cannot borrow ActiveValue::NotSet"),
415 }
416 }
417}
418
419impl<V> PartialEq for ActiveValue<V>
420where
421 V: Into<Value> + std::cmp::PartialEq,
422{
423 fn eq(&self, other: &Self) -> bool {
424 match (self, other) {
425 (ActiveValue::Set(l), ActiveValue::Set(r)) => l == r,
426 (ActiveValue::Unchanged(l), ActiveValue::Unchanged(r)) => l == r,
427 (ActiveValue::NotSet, ActiveValue::NotSet) => true,
428 _ => false,
429 }
430 }
431}
432
433impl<V> From<ActiveValue<V>> for ActiveValue<Option<V>>
434where
435 V: Into<Value> + Nullable,
436{
437 fn from(value: ActiveValue<V>) -> Self {
438 match value {
439 ActiveValue::Set(value) => ActiveValue::set(Some(value)),
440 ActiveValue::Unchanged(value) => ActiveValue::unchanged(Some(value)),
441 ActiveValue::NotSet => ActiveValue::not_set(),
442 }
443 }
444}