diesel/expression_methods/global_expression_methods.rs
1use crate::dsl;
2use crate::expression::array_comparison::{AsInExpression, In, NotIn};
3use crate::expression::grouped::Grouped;
4use crate::expression::operators::*;
5use crate::expression::{assume_not_null, cast, nullable, AsExpression, Expression};
6use crate::sql_types::{SingleValue, SqlType};
7
8/// Methods present on all expressions, except tuples
9pub trait ExpressionMethods: Expression + Sized {
10 /// Creates a SQL `=` expression.
11 ///
12 /// Note that this function follows SQL semantics around `None`/`null` values,
13 /// so `eq(None)` will never match. Use [`is_null`](ExpressionMethods::is_null()) instead.
14 ///
15 #[cfg_attr(
16 any(feature = "sqlite", feature = "postgres"),
17 doc = "To get behavior that is more like the Rust `=` operator you can also use the"
18 )]
19 #[cfg_attr(
20 feature = "sqlite",
21 doc = "sqlite-specific [`is`](crate::SqliteExpressionMethods::is())"
22 )]
23 #[cfg_attr(all(feature = "sqlite", feature = "postgres"), doc = "or the")]
24 #[cfg_attr(
25 feature = "postgres",
26 doc = "postgres-specific [`is_not_distinct_from`](crate::PgExpressionMethods::is_not_distinct_from())"
27 )]
28 #[cfg_attr(any(feature = "sqlite", feature = "postgres"), doc = ".")]
29 ///
30 /// # Example
31 ///
32 /// ```rust
33 /// # include!("../doctest_setup.rs");
34 /// #
35 /// # fn main() {
36 /// # use schema::users::dsl::*;
37 /// # let connection = &mut establish_connection();
38 /// let data = users.select(id).filter(name.eq("Sean"));
39 /// assert_eq!(Ok(1), data.first(connection));
40 /// # }
41 /// ```
42 ///
43 /// Matching against `None` follows SQL semantics:
44 /// ```rust
45 /// # include!("../doctest_setup.rs");
46 /// #
47 /// # fn main() {
48 /// # run_test().unwrap();
49 /// # }
50 /// #
51 /// # fn run_test() -> QueryResult<()> {
52 /// # use schema::animals::dsl::*;
53 /// # let connection = &mut establish_connection();
54 /// #
55 /// let data = animals
56 /// .select(species)
57 /// .filter(name.eq::<Option<String>>(None))
58 /// .first::<String>(connection);
59 /// assert_eq!(Err(diesel::NotFound), data);
60 ///
61 /// let data = animals
62 /// .select(species)
63 /// .filter(name.is_null())
64 /// .first::<String>(connection)?;
65 /// assert_eq!("spider", data);
66 /// # Ok(())
67 /// # }
68 /// ```
69 #[doc(alias = "=")]
70 fn eq<T>(self, other: T) -> dsl::Eq<Self, T>
71 where
72 Self::SqlType: SqlType,
73 T: AsExpression<Self::SqlType>,
74 {
75 Grouped(Eq::new(self, other.as_expression()))
76 }
77
78 /// Creates a SQL `!=` expression.
79 ///
80 /// # Example
81 ///
82 /// ```rust
83 /// # include!("../doctest_setup.rs");
84 /// #
85 /// # fn main() {
86 /// # use schema::users::dsl::*;
87 /// # let connection = &mut establish_connection();
88 /// let data = users.select(id).filter(name.ne("Sean"));
89 /// assert_eq!(Ok(2), data.first(connection));
90 /// # }
91 /// ```
92 #[doc(alias = "<>")]
93 fn ne<T>(self, other: T) -> dsl::NotEq<Self, T>
94 where
95 Self::SqlType: SqlType,
96 T: AsExpression<Self::SqlType>,
97 {
98 Grouped(NotEq::new(self, other.as_expression()))
99 }
100
101 /// Creates a SQL `IN` statement.
102 ///
103 /// Queries using this method will not typically be
104 /// placed in the prepared statement cache. However,
105 /// in cases when a subquery is passed to the method, that
106 /// query will use the cache (assuming the subquery
107 /// itself is safe to cache).
108 /// On PostgreSQL, this method automatically performs a `= ANY()`
109 /// query if this is possible. For cases where this is not possible
110 /// like for example if values is a vector of arrays we
111 /// generate an ordinary `IN` expression instead.
112 ///
113 /// # Example
114 ///
115 /// ```rust
116 /// # include!("../doctest_setup.rs");
117 /// #
118 /// # fn main() {
119 /// # use schema::users;
120 /// # use schema::posts;
121 /// # let connection = &mut establish_connection();
122 /// # diesel::sql_query("INSERT INTO users (name) VALUES
123 /// # ('Jim')").execute(connection).unwrap();
124 /// let data = users::table
125 /// .select(users::id)
126 /// .filter(users::name.eq_any(vec!["Sean", "Jim"]));
127 /// assert_eq!(Ok(vec![1, 3]), data.load(connection));
128 ///
129 /// // Calling `eq_any` with an empty array is the same as doing `WHERE 1=0`
130 /// let data = users::table
131 /// .select(users::id)
132 /// .filter(users::name.eq_any(Vec::<String>::new()));
133 /// assert_eq!(Ok(vec![]), data.load::<i32>(connection));
134 ///
135 /// // Calling `eq_any` with a subquery is the same as using
136 /// // `WHERE {column} IN {subquery}`.
137 ///
138 /// let subquery = users::table
139 /// .filter(users::name.eq("Sean"))
140 /// .select(users::id)
141 /// .into_boxed();
142 /// let data = posts::table
143 /// .select(posts::id)
144 /// .filter(posts::user_id.eq_any(subquery));
145 /// assert_eq!(Ok(vec![1, 2]), data.load::<i32>(connection));
146 ///
147 /// # }
148 /// ```
149 #[doc(alias = "in")]
150 fn eq_any<T>(self, values: T) -> dsl::EqAny<Self, T>
151 where
152 Self::SqlType: SqlType,
153 T: AsInExpression<Self::SqlType>,
154 {
155 Grouped(In::new(self, values.as_in_expression()))
156 }
157
158 /// Creates a SQL `NOT IN` statement.
159 ///
160 /// Queries using this method will not be
161 /// placed in the prepared statement cache. On PostgreSQL, this
162 /// method automatically performs a `!= ALL()` query if this is possible.
163 /// For cases where this is not possible
164 /// like for example if values is a vector of arrays we
165 /// generate an ordinary `NOT IN` expression instead.
166 ///
167 /// # Example
168 ///
169 /// ```rust
170 /// # include!("../doctest_setup.rs");
171 /// #
172 /// # fn main() {
173 /// # use schema::users::dsl::*;
174 /// # let connection = &mut establish_connection();
175 /// # diesel::sql_query("INSERT INTO users (name) VALUES
176 /// # ('Jim')").execute(connection).unwrap();
177 /// let data = users.select(id).filter(name.ne_all(vec!["Sean", "Jim"]));
178 /// assert_eq!(Ok(vec![2]), data.load(connection));
179 ///
180 /// let data = users.select(id).filter(name.ne_all(vec!["Tess"]));
181 /// assert_eq!(Ok(vec![1, 3]), data.load(connection));
182 ///
183 /// // Calling `ne_any` with an empty array is the same as doing `WHERE 1=1`
184 /// let data = users.select(id).filter(name.ne_all(Vec::<String>::new()));
185 /// assert_eq!(Ok(vec![1, 2, 3]), data.load(connection));
186 /// # }
187 /// ```
188 #[doc(alias = "in")]
189 fn ne_all<T>(self, values: T) -> dsl::NeAny<Self, T>
190 where
191 Self::SqlType: SqlType,
192 T: AsInExpression<Self::SqlType>,
193 {
194 Grouped(NotIn::new(self, values.as_in_expression()))
195 }
196
197 /// Creates a SQL `IS NULL` expression.
198 ///
199 /// # Example
200 ///
201 /// ```rust
202 /// # include!("../doctest_setup.rs");
203 /// #
204 /// # fn main() {
205 /// # run_test().unwrap();
206 /// # }
207 /// #
208 /// # fn run_test() -> QueryResult<()> {
209 /// # use schema::animals::dsl::*;
210 /// # let connection = &mut establish_connection();
211 /// #
212 /// let data = animals
213 /// .select(species)
214 /// .filter(name.is_null())
215 /// .first::<String>(connection)?;
216 /// assert_eq!("spider", data);
217 /// # Ok(())
218 /// # }
219 /// ```
220 // This method is part of the public API,
221 // so we cannot just change the name to appease clippy
222 // (Otherwise it's also named after the `IS NULL` sql expression
223 // so that name is really fine)
224 #[allow(clippy::wrong_self_convention)]
225 fn is_null(self) -> dsl::IsNull<Self> {
226 Grouped(IsNull::new(self))
227 }
228
229 /// Creates a SQL `IS NOT NULL` expression.
230 ///
231 /// # Example
232 ///
233 /// ```rust
234 /// # include!("../doctest_setup.rs");
235 /// #
236 /// # fn main() {
237 /// # run_test().unwrap();
238 /// # }
239 /// #
240 /// # fn run_test() -> QueryResult<()> {
241 /// # use schema::animals::dsl::*;
242 /// # let connection = &mut establish_connection();
243 /// #
244 /// let data = animals
245 /// .select(species)
246 /// .filter(name.is_not_null())
247 /// .first::<String>(connection)?;
248 /// assert_eq!("dog", data);
249 /// # Ok(())
250 /// # }
251 /// ```
252 // This method is part of the public API,
253 // so we cannot just change the name to appease clippy
254 // (Otherwise it's also named after the `IS NOT NULL` sql expression
255 // so that name is really fine)
256 #[allow(clippy::wrong_self_convention)]
257 fn is_not_null(self) -> dsl::IsNotNull<Self> {
258 Grouped(IsNotNull::new(self))
259 }
260
261 /// Creates a SQL `>` expression.
262 ///
263 /// # Example
264 ///
265 /// ```rust
266 /// # include!("../doctest_setup.rs");
267 /// #
268 /// # fn main() {
269 /// # run_test().unwrap();
270 /// # }
271 /// #
272 /// # fn run_test() -> QueryResult<()> {
273 /// # use schema::users::dsl::*;
274 /// # let connection = &mut establish_connection();
275 /// let data = users
276 /// .select(name)
277 /// .filter(id.gt(1))
278 /// .first::<String>(connection)?;
279 /// assert_eq!("Tess", data);
280 /// # Ok(())
281 /// # }
282 /// ```
283 #[doc(alias = ">")]
284 fn gt<T>(self, other: T) -> dsl::Gt<Self, T>
285 where
286 Self::SqlType: SqlType,
287 T: AsExpression<Self::SqlType>,
288 {
289 Grouped(Gt::new(self, other.as_expression()))
290 }
291
292 /// Creates a SQL `>=` expression.
293 ///
294 /// # Example
295 ///
296 /// ```rust
297 /// # include!("../doctest_setup.rs");
298 /// #
299 /// # fn main() {
300 /// # run_test().unwrap();
301 /// # }
302 /// #
303 /// # fn run_test() -> QueryResult<()> {
304 /// # use schema::users::dsl::*;
305 /// # let connection = &mut establish_connection();
306 /// let data = users
307 /// .select(name)
308 /// .filter(id.ge(2))
309 /// .first::<String>(connection)?;
310 /// assert_eq!("Tess", data);
311 /// # Ok(())
312 /// # }
313 /// ```
314 #[doc(alias = ">=")]
315 fn ge<T>(self, other: T) -> dsl::GtEq<Self, T>
316 where
317 Self::SqlType: SqlType,
318 T: AsExpression<Self::SqlType>,
319 {
320 Grouped(GtEq::new(self, other.as_expression()))
321 }
322
323 /// Creates a SQL `<` expression.
324 ///
325 /// # Example
326 ///
327 /// ```rust
328 /// # include!("../doctest_setup.rs");
329 /// #
330 /// # fn main() {
331 /// # run_test().unwrap();
332 /// # }
333 /// #
334 /// # fn run_test() -> QueryResult<()> {
335 /// # use schema::users::dsl::*;
336 /// # let connection = &mut establish_connection();
337 /// let data = users
338 /// .select(name)
339 /// .filter(id.lt(2))
340 /// .first::<String>(connection)?;
341 /// assert_eq!("Sean", data);
342 /// # Ok(())
343 /// # }
344 /// ```
345 #[doc(alias = "<")]
346 fn lt<T>(self, other: T) -> dsl::Lt<Self, T>
347 where
348 Self::SqlType: SqlType,
349 T: AsExpression<Self::SqlType>,
350 {
351 Grouped(Lt::new(self, other.as_expression()))
352 }
353
354 /// Creates a SQL `<=` expression.
355 ///
356 /// # Example
357 ///
358 /// ```rust
359 /// # include!("../doctest_setup.rs");
360 /// #
361 /// # fn main() {
362 /// # run_test().unwrap();
363 /// # }
364 /// #
365 /// # fn run_test() -> QueryResult<()> {
366 /// # use schema::users::dsl::*;
367 /// # let connection = &mut establish_connection();
368 /// let data = users
369 /// .select(name)
370 /// .filter(id.le(2))
371 /// .first::<String>(connection)?;
372 /// assert_eq!("Sean", data);
373 /// # Ok(())
374 /// # }
375 /// ```
376 #[doc(alias = "<=")]
377 fn le<T>(self, other: T) -> dsl::LtEq<Self, T>
378 where
379 Self::SqlType: SqlType,
380 T: AsExpression<Self::SqlType>,
381 {
382 Grouped(LtEq::new(self, other.as_expression()))
383 }
384
385 /// Creates a SQL `BETWEEN` expression using the given lower and upper
386 /// bounds.
387 ///
388 /// # Example
389 ///
390 /// ```rust
391 /// # include!("../doctest_setup.rs");
392 /// #
393 /// # fn main() {
394 /// # use schema::animals::dsl::*;
395 /// # let connection = &mut establish_connection();
396 /// #
397 /// let data = animals
398 /// .select(species)
399 /// .filter(legs.between(2, 6))
400 /// .first(connection);
401 /// #
402 /// assert_eq!(Ok("dog".to_string()), data);
403 /// # }
404 /// ```
405 fn between<T, U>(self, lower: T, upper: U) -> dsl::Between<Self, T, U>
406 where
407 Self::SqlType: SqlType,
408 T: AsExpression<Self::SqlType>,
409 U: AsExpression<Self::SqlType>,
410 {
411 Grouped(Between::new(
412 self,
413 And::new(lower.as_expression(), upper.as_expression()),
414 ))
415 }
416
417 /// Creates a SQL `NOT BETWEEN` expression using the given lower and upper
418 /// bounds.
419 ///
420 /// # Example
421 ///
422 /// ```rust
423 /// # include!("../doctest_setup.rs");
424 /// #
425 /// # fn main() {
426 /// # run_test().unwrap();
427 /// # }
428 /// #
429 /// # fn run_test() -> QueryResult<()> {
430 /// # use schema::animals::dsl::*;
431 /// # let connection = &mut establish_connection();
432 /// #
433 /// let data = animals
434 /// .select(species)
435 /// .filter(legs.not_between(2, 6))
436 /// .first::<String>(connection)?;
437 /// assert_eq!("spider", data);
438 /// # Ok(())
439 /// # }
440 /// ```
441 fn not_between<T, U>(self, lower: T, upper: U) -> dsl::NotBetween<Self, T, U>
442 where
443 Self::SqlType: SqlType,
444 T: AsExpression<Self::SqlType>,
445 U: AsExpression<Self::SqlType>,
446 {
447 Grouped(NotBetween::new(
448 self,
449 And::new(lower.as_expression(), upper.as_expression()),
450 ))
451 }
452
453 /// Generates a `CAST(expr AS sql_type)` expression
454 ///
455 /// It is necessary that the expression's SQL type can be casted to the
456 /// target SQL type (represented by the [`CastsTo`](cast::CastsTo) trait),
457 /// and that we know how the corresponding SQL type is named for the
458 /// specific backend (represented by the
459 /// [`KnownCastSqlTypeName`](cast::KnownCastSqlTypeName) trait).
460 ///
461 /// # Example
462 ///
463 /// ```rust
464 /// # include!("../doctest_setup.rs");
465 /// #
466 /// # fn main() {
467 /// # run_test().unwrap();
468 /// # }
469 /// #
470 /// # fn run_test() -> QueryResult<()> {
471 /// # use schema::animals::dsl::*;
472 /// # let connection = &mut establish_connection();
473 /// #
474 /// use diesel::sql_types;
475 ///
476 /// let data = diesel::select(
477 /// 12_i32
478 /// .into_sql::<sql_types::Int4>()
479 /// .cast::<sql_types::Text>(),
480 /// )
481 /// .first::<String>(connection)?;
482 /// assert_eq!("12", data);
483 /// # Ok(())
484 /// # }
485 /// ```
486 fn cast<ST>(self) -> dsl::Cast<Self, ST>
487 where
488 ST: SingleValue,
489 Self::SqlType: cast::CastsTo<ST>,
490 {
491 cast::Cast::new(self)
492 }
493
494 /// Generates a `CAST(expr AS sql_type)` expression, this version does not check the castability, like [`cast()`](Self::cast).
495 ///
496 /// # Example
497 ///
498 /// ```rust
499 /// # include!("../doctest_setup.rs");
500 /// #
501 /// # fn main() {
502 /// # run_test().unwrap();
503 /// # }
504 /// #
505 /// # fn run_test() -> QueryResult<()> {
506 /// # use schema::animals::dsl::*;
507 /// # let connection = &mut establish_connection();
508 /// #
509 /// use diesel::sql_types;
510 ///
511 /// let data = diesel::select(
512 /// "12".into_sql::<sql_types::Text>()
513 /// .fallible_cast::<sql_types::Int8>(),
514 /// )
515 /// .first::<i64>(connection)?;
516 /// assert_eq!(12i64, data);
517 /// # Ok(())
518 /// # }
519 /// ```
520 fn fallible_cast<ST>(self) -> dsl::Cast<Self, ST>
521 where
522 ST: SingleValue,
523 Self::SqlType: cast::FallibleCastsTo<ST>,
524 {
525 cast::Cast::new(self)
526 }
527
528 /// Creates a SQL `DESC` expression, representing this expression in
529 /// descending order.
530 ///
531 /// # Example
532 ///
533 /// ```rust
534 /// # include!("../doctest_setup.rs");
535 /// #
536 /// # fn main() {
537 /// # run_test().unwrap();
538 /// # }
539 /// #
540 /// # fn run_test() -> QueryResult<()> {
541 /// # use schema::users::dsl::*;
542 /// # let connection = &mut establish_connection();
543 /// #
544 /// let names = users
545 /// .select(name)
546 /// .order(name.desc())
547 /// .load::<String>(connection)?;
548 /// assert_eq!(vec!["Tess", "Sean"], names);
549 /// # Ok(())
550 /// # }
551 /// ```
552 fn desc(self) -> dsl::Desc<Self> {
553 Desc::new(self)
554 }
555
556 /// Creates a SQL `ASC` expression, representing this expression in
557 /// ascending order.
558 ///
559 /// This is the same as leaving the direction unspecified. It is useful if
560 /// you need to provide an unknown ordering, and need to box the return
561 /// value of a function.
562 ///
563 /// # Example
564 ///
565 /// ```rust
566 /// # include!("../doctest_setup.rs");
567 /// # use diesel::expression::expression_types::NotSelectable;
568 /// #
569 /// # fn main() {
570 /// # use schema::users::dsl::*;
571 /// # let order = "name";
572 /// let ordering: Box<dyn BoxableExpression<users, DB, SqlType = NotSelectable>> =
573 /// if order == "name" {
574 /// Box::new(name.desc())
575 /// } else {
576 /// Box::new(id.asc())
577 /// };
578 /// # }
579 /// ```
580 fn asc(self) -> dsl::Asc<Self> {
581 Asc::new(self)
582 }
583}
584
585impl<T> ExpressionMethods for T
586where
587 T: Expression,
588 T::SqlType: SingleValue,
589{
590}
591
592/// Methods present on all expressions
593pub trait NullableExpressionMethods: Expression + Sized {
594 /// Converts this potentially non-null expression into one which is treated
595 /// as nullable. This method has no impact on the generated SQL, and is only
596 /// used to allow certain comparisons that would otherwise fail to compile.
597 ///
598 /// # Example
599 /// ```no_run
600 /// # #![allow(dead_code)]
601 /// # include!("../doctest_setup.rs");
602 /// # use diesel::sql_types::*;
603 /// # use schema::users;
604 /// #
605 /// table! {
606 /// posts {
607 /// id -> Integer,
608 /// user_id -> Integer,
609 /// author_name -> Nullable<VarChar>,
610 /// }
611 /// }
612 /// #
613 /// # joinable!(posts -> users (user_id));
614 /// # allow_tables_to_appear_in_same_query!(posts, users);
615 ///
616 /// fn main() {
617 /// use self::posts::dsl::{author_name, posts};
618 /// use self::users::dsl::*;
619 /// let connection = &mut establish_connection();
620 ///
621 /// let data = users
622 /// .inner_join(posts)
623 /// .filter(name.nullable().eq(author_name))
624 /// .select(name)
625 /// .load::<String>(connection);
626 /// println!("{:?}", data);
627 /// }
628 /// ```
629 fn nullable(self) -> dsl::Nullable<Self> {
630 nullable::Nullable::new(self)
631 }
632
633 /// Converts this potentially nullable expression into one which will be **assumed**
634 /// to be not-null. This method has no impact on the generated SQL, however it will
635 /// enable you to attempt deserialization of the returned value in a non-`Option`.
636 ///
637 /// This is meant to cover for cases where you know that given the `WHERE` clause
638 /// the field returned by the database will never be `NULL`.
639 ///
640 /// This **will cause runtime errors** on `load()` if the "assume" turns out to be incorrect.
641 ///
642 /// # Examples
643 /// ## Normal usage
644 /// ```rust
645 /// # #![allow(dead_code)]
646 /// # include!("../doctest_setup.rs");
647 /// # use diesel::sql_types::*;
648 /// #
649 /// table! {
650 /// animals {
651 /// id -> Integer,
652 /// species -> VarChar,
653 /// legs -> Integer,
654 /// name -> Nullable<VarChar>,
655 /// }
656 /// }
657 ///
658 /// fn main() {
659 /// use self::animals::dsl::*;
660 /// let connection = &mut establish_connection();
661 ///
662 /// let result = animals
663 /// .filter(name.is_not_null())
664 /// .select(name.assume_not_null())
665 /// .load::<String>(connection);
666 /// assert!(result.is_ok());
667 /// }
668 /// ```
669 ///
670 /// ## Incorrect usage
671 /// ```rust
672 /// # #![allow(dead_code)]
673 /// # include!("../doctest_setup.rs");
674 /// # use diesel::sql_types::*;
675 /// #
676 /// table! {
677 /// animals {
678 /// id -> Integer,
679 /// species -> VarChar,
680 /// legs -> Integer,
681 /// name -> Nullable<VarChar>,
682 /// }
683 /// }
684 ///
685 /// fn main() {
686 /// use diesel::result::{Error, UnexpectedNullError};
687 /// use self::animals::dsl::*;
688 /// let connection = &mut establish_connection();
689 ///
690 /// let result = animals
691 /// .select(name.assume_not_null())
692 /// .load::<String>(connection);
693 /// assert!(matches!(
694 /// result,
695 /// Err(Error::DeserializationError(err)) if err.is::<UnexpectedNullError>()
696 /// ));
697 /// }
698 /// ```
699 ///
700 /// ## Advanced usage - use only if you're sure you know what you're doing!
701 ///
702 /// This will cause the `Option` to be `None` where the `left_join` succeeded but the
703 /// `author_name` turned out to be `NULL`, due to how `Option` deserialization works.
704 /// (see [`Queryable` documentation](crate::deserialize::Queryable))
705 ///
706 /// ```rust
707 /// # #![allow(dead_code)]
708 /// # include!("../doctest_setup.rs");
709 /// # use diesel::sql_types::*;
710 /// # use schema::users;
711 /// #
712 /// table! {
713 /// posts {
714 /// id -> Integer,
715 /// user_id -> Integer,
716 /// author_name -> Nullable<Text>,
717 /// }
718 /// }
719 /// #
720 /// # joinable!(posts -> users (user_id));
721 /// # allow_tables_to_appear_in_same_query!(posts, users);
722 ///
723 /// fn main() {
724 /// use self::posts;
725 /// use self::users;
726 /// let connection = &mut establish_connection();
727 ///
728 /// # diesel::sql_query("ALTER TABLE posts ADD COLUMN author_name Text")
729 /// # .execute(connection)
730 /// # .unwrap();
731 /// # diesel::update(posts::table.filter(posts::user_id.eq(1)))
732 /// # .set(posts::author_name.eq("Sean"))
733 /// # .execute(connection);
734 ///
735 /// let result = posts::table
736 /// .left_join(users::table)
737 /// .select((
738 /// posts::id,
739 /// (users::id, posts::author_name.assume_not_null()).nullable(),
740 /// ))
741 /// .order_by(posts::id)
742 /// .load::<(i32, Option<(i32, String)>)>(connection);
743 /// let expected = Ok(vec![
744 /// (1, Some((1, "Sean".to_owned()))),
745 /// (2, Some((1, "Sean".to_owned()))),
746 /// (3, None),
747 /// ]);
748 /// assert_eq!(expected, result);
749 /// }
750 /// ```
751 fn assume_not_null(self) -> dsl::AssumeNotNull<Self> {
752 assume_not_null::AssumeNotNull::new(self)
753 }
754}
755
756impl<T: Expression> NullableExpressionMethods for T {}