diesel/expression/functions/window_functions.rs
1#[cfg(doc)]
2use super::aggregate_expressions::WindowExpressionMethods;
3use crate::sql_types::helper::CombinedNullableValue;
4use crate::sql_types::{Integer, IntoNotNullable, IntoNullable, SingleValue, SqlType};
5use diesel_derives::declare_sql_function;
6
7#[declare_sql_function]
8extern "SQL" {
9
10 /// Number of th current row within its partition
11 ///
12 /// Returns the number of the current row within its partition, counting from 1.
13 ///
14 /// This function must be used as window function. You need to call at least one
15 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
16 /// clause. It cannot be used outside of `SELECT` clauses.
17 ///
18 /// ```
19 /// # include!("../../doctest_setup.rs");
20 /// # use diesel::dsl::*;
21 /// #
22 /// # fn main() -> QueryResult<()> {
23 /// # use schema::posts::dsl::*;
24 /// # let connection = &mut establish_connection();
25 /// let res = posts
26 /// .select((title, user_id, row_number().partition_by(user_id)))
27 /// .load::<(String, i32, i64)>(connection)?;
28 /// let expected = vec![
29 /// ("My first post".to_owned(), 1, 1),
30 /// ("About Rust".into(), 1, 2),
31 /// ("My first post too".into(), 2, 1),
32 /// ];
33 /// assert_eq!(expected, res);
34 /// # Ok(())
35 /// # }
36 /// ```
37 #[window]
38 fn row_number() -> BigInt;
39
40 /// Rank of current row within its partition, with gaps
41 ///
42 /// Returns the rank of the current row, with gaps;
43 /// that is, the row_number of the first row in its peer group.
44 ///
45 /// This function must be used as window function. You need to call at least one
46 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
47 /// clause. It cannot be used outside of `SELECT` clauses.
48 ///
49 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
50 ///
51 /// ```
52 /// # include!("../../doctest_setup.rs");
53 /// # use diesel::dsl::*;
54 /// #
55 /// # fn main() -> QueryResult<()> {
56 /// # use schema::posts::dsl::*;
57 /// # let connection = &mut establish_connection();
58 /// let res = posts
59 /// .select((
60 /// title,
61 /// user_id,
62 /// rank().partition_by(user_id).window_order(user_id),
63 /// ))
64 /// .load::<(String, i32, i64)>(connection)?;
65 /// let expected = vec![
66 /// ("My first post".to_owned(), 1, 1),
67 /// ("About Rust".into(), 1, 1),
68 /// ("My first post too".into(), 2, 1),
69 /// ];
70 /// assert_eq!(expected, res);
71 /// # Ok(())
72 /// # }
73 /// ```
74 #[window(dialect(
75 BuiltInWindowFunctionRequireOrder,
76 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
77 ))]
78 #[cfg_attr(
79 feature = "mysql_backend",
80 window(backends(diesel::mysql::Mysql), require_order = true)
81 )]
82 #[cfg_attr(
83 feature = "mariadb_backend",
84 window(backends(diesel::mariadb::Mariadb), require_order = true)
85 )]
86 fn rank() -> BigInt;
87
88 /// Rank of current row within its partition, without gaps
89 ///
90 /// Returns the rank of the current row, without gaps;
91 /// this function effectively counts peer groups.
92 ///
93 /// This function must be used as window function. You need to call at least one
94 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
95 /// clause. It cannot be used outside of `SELECT` clauses.
96 ///
97 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
98 ///
99 /// ```
100 /// # include!("../../doctest_setup.rs");
101 /// # use diesel::dsl::*;
102 /// #
103 /// # fn main() -> QueryResult<()> {
104 /// # use schema::posts::dsl::*;
105 /// # let connection = &mut establish_connection();
106 /// let res = posts
107 /// .select((
108 /// title,
109 /// user_id,
110 /// dense_rank().partition_by(user_id).window_order(user_id),
111 /// ))
112 /// .load::<(String, i32, i64)>(connection)?;
113 /// let expected = vec![
114 /// ("My first post".to_owned(), 1, 1),
115 /// ("About Rust".into(), 1, 1),
116 /// ("My first post too".into(), 2, 1),
117 /// ];
118 /// assert_eq!(expected, res);
119 /// # Ok(())
120 /// # }
121 /// ```
122 #[window(dialect(
123 BuiltInWindowFunctionRequireOrder,
124 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
125 ))]
126 #[cfg_attr(
127 feature = "mysql_backend",
128 window(backends(diesel::mysql::Mysql), require_order = true)
129 )]
130 #[cfg_attr(
131 feature = "mariadb_backend",
132 window(backends(diesel::mariadb::Mariadb), require_order = true)
133 )]
134 fn dense_rank() -> BigInt;
135
136 /// Percentage rank value
137 ///
138 /// Returns the relative rank of the current row,
139 /// that is (rank - 1) / (total partition rows - 1).
140 /// The value thus ranges from 0 to 1 inclusive.
141 ///
142 /// This function must be used as window function. You need to call at least one
143 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
144 /// clause. It cannot be used outside of `SELECT` clauses.
145 ///
146 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
147 ///
148 /// ```
149 /// # include!("../../doctest_setup.rs");
150 /// # use diesel::dsl::*;
151 /// #
152 /// # fn main() -> QueryResult<()> {
153 /// # use schema::posts::dsl::*;
154 /// # let connection = &mut establish_connection();
155 /// let res = posts
156 /// .select((
157 /// title,
158 /// user_id,
159 /// percent_rank().partition_by(user_id).window_order(user_id),
160 /// ))
161 /// .load::<(String, i32, f64)>(connection)?;
162 /// let expected = vec![
163 /// ("My first post".to_owned(), 1, 0.0),
164 /// ("About Rust".into(), 1, 0.0),
165 /// ("My first post too".into(), 2, 0.0),
166 /// ];
167 /// assert_eq!(expected, res);
168 /// # Ok(())
169 /// # }
170 /// ```
171 #[window(dialect(
172 BuiltInWindowFunctionRequireOrder,
173 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
174 ))]
175 #[cfg_attr(
176 feature = "mysql_backend",
177 window(backends(diesel::mysql::Mysql), require_order = true)
178 )]
179 #[cfg_attr(
180 feature = "mariadb_backend",
181 window(backends(diesel::mariadb::Mariadb), require_order = true)
182 )]
183 fn percent_rank() -> Double;
184
185 /// Cumulative distribution value
186 ///
187 /// Returns the cumulative distribution,
188 /// that is (number of partition rows preceding or peers with current row) / (total partition rows).
189 /// The value thus ranges from 1/N to 1.
190 ///
191 /// This function must be used as window function. You need to call at least one
192 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
193 /// clause. It cannot be used outside of `SELECT` clauses.
194 ///
195 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
196 ///
197 /// ```
198 /// # include!("../../doctest_setup.rs");
199 /// # use diesel::dsl::*;
200 /// #
201 /// # fn main() -> QueryResult<()> {
202 /// # use schema::posts::dsl::*;
203 /// # let connection = &mut establish_connection();
204 /// let res = posts
205 /// .select((
206 /// title,
207 /// user_id,
208 /// cume_dist().partition_by(user_id).window_order(user_id),
209 /// ))
210 /// .load::<(String, i32, f64)>(connection)?;
211 /// let expected = vec![
212 /// ("My first post".to_owned(), 1, 1.0),
213 /// ("About Rust".into(), 1, 1.0),
214 /// ("My first post too".into(), 2, 1.0),
215 /// ];
216 /// assert_eq!(expected, res);
217 /// # Ok(())
218 /// # }
219 /// ```
220 #[window(dialect(
221 BuiltInWindowFunctionRequireOrder,
222 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
223 ))]
224 #[cfg_attr(
225 feature = "mysql_backend",
226 window(backends(diesel::mysql::Mysql), require_order = true)
227 )]
228 #[cfg_attr(
229 feature = "mariadb_backend",
230 window(backends(diesel::mariadb::Mariadb), require_order = true)
231 )]
232 fn cume_dist() -> Double;
233
234 /// Bucket number of current row within its partition
235 ///
236 /// Returns an integer ranging from 1 to the argument value,
237 /// dividing the partition as equally as possible.
238 ///
239 ///
240 /// This function must be used as window function. You need to call at least one
241 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
242 /// clause. It cannot be used outside of `SELECT` clauses.
243 ///
244 /// ```
245 /// # include!("../../doctest_setup.rs");
246 /// # use diesel::dsl::*;
247 /// #
248 /// # fn main() -> QueryResult<()> {
249 /// # use schema::posts::dsl::*;
250 /// # let connection = &mut establish_connection();
251 /// let res = posts
252 /// .select((title, user_id, ntile(2).partition_by(user_id)))
253 /// .load::<(String, i32, i32)>(connection)?;
254 /// let expected = vec![
255 /// ("My first post".to_owned(), 1, 1),
256 /// ("About Rust".into(), 1, 2),
257 /// ("My first post too".into(), 2, 1),
258 /// ];
259 /// assert_eq!(expected, res);
260 /// # Ok(())
261 /// # }
262 /// ```
263 #[window]
264 fn ntile(num_buckets: Integer) -> Integer;
265
266 /// Value of argument from row lagging current row within partition
267 ///
268 /// Returns value evaluated at the row that is one row before the current
269 /// row within the partition. If there is no such row, NULL is returned instead.
270 ///
271 /// See [`lag_with_offset`] and [`lag_with_offset_and_default`] for variants with configurable offset
272 /// and default values.
273 ///
274 /// This function must be used as window function. You need to call at least one
275 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
276 /// clause. It cannot be used outside of `SELECT` clauses.
277 ///
278 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
279 ///
280 /// ```
281 /// # include!("../../doctest_setup.rs");
282 /// # use diesel::dsl::*;
283 /// #
284 /// # fn main() -> QueryResult<()> {
285 /// # use schema::posts::dsl::*;
286 /// # let connection = &mut establish_connection();
287 /// let res = posts
288 /// .select((
289 /// title,
290 /// user_id,
291 /// lag(id).partition_by(user_id).window_order(user_id),
292 /// ))
293 /// .load::<(String, i32, Option<i32>)>(connection)?;
294 /// let expected = vec![
295 /// ("My first post".to_owned(), 1, None),
296 /// ("About Rust".into(), 1, Some(1)),
297 /// ("My first post too".into(), 2, None),
298 /// ];
299 /// assert_eq!(expected, res);
300 /// # Ok(())
301 /// # }
302 /// ```
303 #[window(dialect(
304 BuiltInWindowFunctionRequireOrder,
305 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
306 ))]
307 #[cfg_attr(
308 feature = "mysql_backend",
309 window(backends(diesel::mysql::Mysql), require_order = true)
310 )]
311 #[cfg_attr(
312 feature = "mariadb_backend",
313 window(backends(diesel::mariadb::Mariadb), require_order = true)
314 )]
315 fn lag<T: SqlType + SingleValue + IntoNullable<Nullable: SingleValue>>(value: T)
316 -> T::Nullable;
317
318 /// Value of argument from row lagging current row within partition
319 ///
320 /// Returns value evaluated at the row that is offset rows before the current
321 /// row within the partition; If there is no such row, NULL is returned instead.
322 ///
323 /// This function must be used as window function. You need to call at least one
324 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
325 /// clause. It cannot be used outside of `SELECT` clauses.
326 ///
327 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
328 ///
329 /// ```
330 /// # include!("../../doctest_setup.rs");
331 /// # use diesel::dsl::*;
332 /// #
333 /// # fn main() -> QueryResult<()> {
334 /// # use schema::posts::dsl::*;
335 /// # let connection = &mut establish_connection();
336 /// let res = posts
337 /// .select((
338 /// title,
339 /// user_id,
340 /// lag_with_offset(id, 1)
341 /// .partition_by(user_id)
342 /// .window_order(user_id),
343 /// ))
344 /// .load::<(String, i32, Option<i32>)>(connection)?;
345 /// let expected = vec![
346 /// ("My first post".to_owned(), 1, None),
347 /// ("About Rust".into(), 1, Some(1)),
348 /// ("My first post too".into(), 2, None),
349 /// ];
350 /// assert_eq!(expected, res);
351 /// # Ok(())
352 /// # }
353 /// ```
354 #[doc(alias = "lag")]
355 #[sql_name = "lag"]
356 #[window(dialect(
357 BuiltInWindowFunctionRequireOrder,
358 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
359 ))]
360 #[cfg_attr(
361 feature = "mysql_backend",
362 window(backends(diesel::mysql::Mysql), require_order = true)
363 )]
364 #[cfg_attr(
365 feature = "mariadb_backend",
366 window(backends(diesel::mariadb::Mariadb), require_order = true)
367 )]
368 fn lag_with_offset<T: SqlType + SingleValue + IntoNullable<Nullable: SingleValue>>(
369 value: T,
370 offset: Integer,
371 ) -> T::Nullable;
372
373 /// Value of argument from row lagging current row within partition
374 ///
375 /// Returns value evaluated at the row that is offset rows before the current
376 /// row within the partition; if there is no such row, instead returns default
377 /// (which must be of a type compatible with value).
378 /// Both offset and default are evaluated with respect to the current row.
379 /// If omitted, offset defaults to 1 and default to NULL.
380 ///
381 /// This function returns a nullable value if either the value or the default expression are
382 /// nullable.
383 ///
384 /// This function must be used as window function. You need to call at least one
385 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
386 /// clause. It cannot be used outside of `SELECT` clauses.
387 ///
388 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
389 ///
390 /// ```
391 /// # include!("../../doctest_setup.rs");
392 /// # use diesel::dsl::*;
393 /// #
394 /// # #[cfg(not(feature = "mariadb"))]
395 /// # fn main() -> QueryResult<()> {
396 /// # use schema::posts::dsl::*;
397 /// # use diesel::sql_types::{Integer, Nullable};
398 /// # let connection = &mut establish_connection();
399 /// let res = posts
400 /// .select((
401 /// title,
402 /// user_id,
403 /// lag_with_offset_and_default(id, 1, user_id)
404 /// .partition_by(user_id)
405 /// .window_order(user_id),
406 /// ))
407 /// .load::<(String, i32, i32)>(connection)?;
408 /// let expected = vec![
409 /// ("My first post".to_owned(), 1, 1),
410 /// ("About Rust".into(), 1, 1),
411 /// ("My first post too".into(), 2, 2),
412 /// ];
413 /// assert_eq!(expected, res);
414 ///
415 /// let res = posts
416 /// .select((
417 /// title,
418 /// user_id,
419 /// lag_with_offset_and_default(None::<i32>.into_sql::<Nullable<Integer>>(), 1, user_id)
420 /// .partition_by(user_id)
421 /// .window_order(user_id),
422 /// ))
423 /// .load::<(String, i32, Option<i32>)>(connection)?;
424 /// let expected = vec![
425 /// ("My first post".to_owned(), 1, Some(1)),
426 /// ("About Rust".into(), 1, None),
427 /// ("My first post too".into(), 2, Some(2)),
428 /// ];
429 /// assert_eq!(expected, res);
430 ///
431 /// let res = posts
432 /// .select((
433 /// title,
434 /// user_id,
435 /// lag_with_offset_and_default(id, 1, None::<i32>.into_sql::<Nullable<Integer>>())
436 /// .partition_by(user_id)
437 /// .window_order(user_id),
438 /// ))
439 /// .load::<(String, i32, Option<i32>)>(connection)?;
440 /// let expected = vec![
441 /// ("My first post".to_owned(), 1, None),
442 /// ("About Rust".into(), 1, Some(1)),
443 /// ("My first post too".into(), 2, None),
444 /// ];
445 /// assert_eq!(expected, res);
446 /// # Ok(())
447 /// # }
448 /// # #[cfg(feature = "mariadb")]
449 /// # fn main() {}
450 /// ```
451 #[doc(alias = "lag")]
452 #[sql_name = "lag"]
453 #[window(dialect(
454 BuiltInWindowFunctionRequireOrder,
455 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
456 ))]
457 #[cfg_attr(
458 feature = "mysql_backend",
459 window(backends(diesel::mysql::Mysql), require_order = true)
460 )]
461 #[cfg_attr(
462 feature = "mariadb_backend",
463 window(backends(diesel::mariadb::Mariadb), require_order = true)
464 )]
465 fn lag_with_offset_and_default<
466 T: SqlType
467 + SingleValue
468 + IntoNotNullable<NotNullable: self::private::SameType<T2::NotNullable>>
469 + CombinedNullableValue<T2, T::NotNullable>,
470 T2: SqlType + SingleValue + IntoNotNullable,
471 >(
472 value: T,
473 offset: Integer,
474 default: T2,
475 ) -> T::Out;
476
477 /// Value of argument from row leading current row within partition
478 ///
479 /// Returns value evaluated at the row that is offset rows after the current
480 /// row within the partition; if there is no such row,
481 /// `NULL` will be returned instead.
482 ///
483 /// See [`lead_with_offset`] and [`lead_with_offset_and_default`] for variants with configurable offset
484 /// and default values.
485 ///
486 /// This function must be used as window function. You need to call at least one
487 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
488 /// clause. It cannot be used outside of `SELECT` clauses.
489 ///
490 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
491 ///
492 /// ```
493 /// # include!("../../doctest_setup.rs");
494 /// # use diesel::dsl::*;
495 /// #
496 /// # fn main() -> QueryResult<()> {
497 /// # use schema::posts::dsl::*;
498 /// # let connection = &mut establish_connection();
499 /// let res = posts
500 /// .select((
501 /// title,
502 /// user_id,
503 /// lead(id).partition_by(user_id).window_order(user_id),
504 /// ))
505 /// .load::<(String, i32, Option<i32>)>(connection)?;
506 /// let expected = vec![
507 /// ("My first post".to_owned(), 1, Some(2)),
508 /// ("About Rust".into(), 1, None),
509 /// ("My first post too".into(), 2, None),
510 /// ];
511 /// assert_eq!(expected, res);
512 /// # Ok(())
513 /// # }
514 /// ```
515 #[window(dialect(
516 BuiltInWindowFunctionRequireOrder,
517 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
518 ))]
519 #[cfg_attr(
520 feature = "mysql_backend",
521 window(backends(diesel::mysql::Mysql), require_order = true)
522 )]
523 #[cfg_attr(
524 feature = "mariadb_backend",
525 window(backends(diesel::mariadb::Mariadb), require_order = true)
526 )]
527 fn lead<T: SqlType + SingleValue + IntoNullable<Nullable: SingleValue>>(
528 value: T,
529 ) -> T::Nullable;
530
531 /// Value of argument from row leading current row within partition
532 ///
533 /// Returns value evaluated at the row that is offset rows after the current
534 /// row within the partition; if there is no such row,
535 /// `NULL` is returned instead
536 ///
537 ///
538 /// This function must be used as window function. You need to call at least one
539 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
540 /// clause. It cannot be used outside of `SELECT` clauses.
541 ///
542 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
543 ///
544 /// ```
545 /// # include!("../../doctest_setup.rs");
546 /// # use diesel::dsl::*;
547 /// #
548 /// # fn main() -> QueryResult<()> {
549 /// # use schema::posts::dsl::*;
550 /// # let connection = &mut establish_connection();
551 /// let res = posts
552 /// .select((
553 /// title,
554 /// user_id,
555 /// lead_with_offset(id, 1)
556 /// .partition_by(user_id)
557 /// .window_order(user_id),
558 /// ))
559 /// .load::<(String, i32, Option<i32>)>(connection)?;
560 /// let expected = vec![
561 /// ("My first post".to_owned(), 1, Some(2)),
562 /// ("About Rust".into(), 1, None),
563 /// ("My first post too".into(), 2, None),
564 /// ];
565 /// assert_eq!(expected, res);
566 /// # Ok(())
567 /// # }
568 /// ```
569 #[doc(alias = "lead")]
570 #[sql_name = "lead"]
571 #[window(dialect(
572 BuiltInWindowFunctionRequireOrder,
573 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
574 ))]
575 #[cfg_attr(
576 feature = "mysql_backend",
577 window(backends(diesel::mysql::Mysql), require_order = true)
578 )]
579 #[cfg_attr(
580 feature = "mariadb_backend",
581 window(backends(diesel::mariadb::Mariadb), require_order = true)
582 )]
583 fn lead_with_offset<T: SqlType + SingleValue + IntoNullable<Nullable: SingleValue>>(
584 value: T,
585 offset: Integer,
586 ) -> T::Nullable;
587
588 /// Value of argument from row leading current row within partition
589 ///
590 /// Returns value evaluated at the row that is offset rows after the current
591 /// row within the partition; if there is no such row,
592 /// instead returns default (which must be of a type compatible with value).
593 /// Both offset and default are evaluated with respect to the current row.
594 /// If omitted, offset defaults to 1 and default to NULL.
595 ///
596 /// This function returns a nullable value if either the value or the default expression are
597 /// nullable.
598 ///
599 /// This function must be used as window function. You need to call at least one
600 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
601 /// clause. It cannot be used outside of `SELECT` clauses.
602 ///
603 /// For MySQL this function requires you to call [`.window_order()`](WindowExpressionMethods::window_order())
604 ///
605 /// ```
606 /// # include!("../../doctest_setup.rs");
607 /// # use diesel::dsl::*;
608 /// #
609 /// # #[cfg(not(feature = "mariadb"))]
610 /// # fn main() -> QueryResult<()> {
611 /// # use schema::posts::dsl::*;
612 /// # use diesel::sql_types::{Integer, Nullable};
613 /// # let connection = &mut establish_connection();
614 /// let res = posts
615 /// .select((
616 /// title,
617 /// user_id,
618 /// lead_with_offset_and_default(id, 1, user_id)
619 /// .partition_by(user_id)
620 /// .window_order(user_id),
621 /// ))
622 /// .load::<(String, i32, i32)>(connection)?;
623 /// let expected = vec![
624 /// ("My first post".to_owned(), 1, 2),
625 /// ("About Rust".into(), 1, 1),
626 /// ("My first post too".into(), 2, 2),
627 /// ];
628 /// assert_eq!(expected, res);
629 ///
630 /// let res = posts
631 /// .select((
632 /// title,
633 /// user_id,
634 /// lead_with_offset_and_default(None::<i32>.into_sql::<Nullable<Integer>>(), 1, user_id)
635 /// .partition_by(user_id)
636 /// .window_order(user_id),
637 /// ))
638 /// .load::<(String, i32, Option<i32>)>(connection)?;
639 /// let expected = vec![
640 /// ("My first post".to_owned(), 1, None),
641 /// ("About Rust".into(), 1, Some(1)),
642 /// ("My first post too".into(), 2, Some(2)),
643 /// ];
644 /// assert_eq!(expected, res);
645 ///
646 /// let res = posts
647 /// .select((
648 /// title,
649 /// user_id,
650 /// lead_with_offset_and_default(id, 1, None::<i32>.into_sql::<Nullable<Integer>>())
651 /// .partition_by(user_id)
652 /// .window_order(user_id),
653 /// ))
654 /// .load::<(String, i32, Option<i32>)>(connection)?;
655 /// let expected = vec![
656 /// ("My first post".to_owned(), 1, Some(2)),
657 /// ("About Rust".into(), 1, None),
658 /// ("My first post too".into(), 2, None),
659 /// ];
660 /// assert_eq!(expected, res);
661 /// # Ok(())
662 /// # }
663 /// # #[cfg(feature = "mariadb")]
664 /// fn main() {}
665 /// ```
666 #[doc(alias = "lead")]
667 #[sql_name = "lead"]
668 #[window(dialect(
669 BuiltInWindowFunctionRequireOrder,
670 crate::backend::sql_dialect::built_in_window_function_require_order::NoOrderRequired
671 ))]
672 #[cfg_attr(
673 feature = "mysql_backend",
674 window(backends(diesel::mysql::Mysql), require_order = true)
675 )]
676 #[cfg_attr(
677 feature = "mariadb_backend",
678 window(backends(diesel::mariadb::Mariadb), require_order = true)
679 )]
680 fn lead_with_offset_and_default<
681 T: SqlType
682 + SingleValue
683 + IntoNotNullable<NotNullable: self::private::SameType<T2::NotNullable>>
684 + CombinedNullableValue<T2, T::NotNullable>,
685 T2: SqlType + SingleValue + IntoNotNullable,
686 >(
687 value: T,
688 offset: Integer,
689 default: T2,
690 ) -> T::Out;
691
692 /// Value of argument from first row of window frame
693 ///
694 /// Returns value evaluated at the row that is the first row of the window frame.
695 ///
696 ///
697 /// This function must be used as window function. You need to call at least one
698 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
699 /// clause. It cannot be used outside of `SELECT` clauses.
700 ///
701 /// ```
702 /// # include!("../../doctest_setup.rs");
703 /// # use diesel::dsl::*;
704 /// #
705 /// # fn main() -> QueryResult<()> {
706 /// # use schema::posts::dsl::*;
707 /// # let connection = &mut establish_connection();
708 /// let res = posts
709 /// .select((title, user_id, first_value(id).partition_by(user_id)))
710 /// .load::<(String, i32, i32)>(connection)?;
711 /// let expected = vec![
712 /// ("My first post".to_owned(), 1, 1),
713 /// ("About Rust".into(), 1, 1),
714 /// ("My first post too".into(), 2, 3),
715 /// ];
716 /// assert_eq!(expected, res);
717 /// # Ok(())
718 /// # }
719 /// ```
720 #[window]
721 fn first_value<T: SqlType + SingleValue>(value: T) -> T;
722
723 /// Value of argument from last row of window frame
724 ///
725 /// Returns value evaluated at the row that is the last row of the window frame.
726 ///
727 ///
728 /// This function must be used as window function. You need to call at least one
729 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
730 /// clause. It cannot be used outside of `SELECT` clauses.
731 ///
732 /// ```
733 /// # include!("../../doctest_setup.rs");
734 /// # use diesel::dsl::*;
735 /// #
736 /// # fn main() -> QueryResult<()> {
737 /// # use schema::posts::dsl::*;
738 /// # let connection = &mut establish_connection();
739 /// let res = posts
740 /// .select((title, user_id, last_value(id).partition_by(user_id)))
741 /// .load::<(String, i32, i32)>(connection)?;
742 /// let expected = vec![
743 /// ("My first post".to_owned(), 1, 2),
744 /// ("About Rust".into(), 1, 2),
745 /// ("My first post too".into(), 2, 3),
746 /// ];
747 /// assert_eq!(expected, res);
748 /// # Ok(())
749 /// # }
750 /// ```
751 #[window]
752 fn last_value<T: SqlType + SingleValue>(value: T) -> T;
753
754 /// Value of argument from N-th row of window frame
755 ///
756 /// Returns value evaluated at the row that is the n'th row of the window frame (counting from 1);
757 /// returns NULL if there is no such row.
758 ///
759 ///
760 /// This function must be used as window function. You need to call at least one
761 /// of the methods [`WindowExpressionMethods`] from to use this function in your `SELECT`
762 /// clause. It cannot be used outside of `SELECT` clauses.
763 ///
764 /// ```
765 /// # include!("../../doctest_setup.rs");
766 /// # use diesel::dsl::*;
767 /// #
768 /// # fn main() -> QueryResult<()> {
769 /// # use schema::posts::dsl::*;
770 /// # let connection = &mut establish_connection();
771 /// let res = posts
772 /// .select((title, user_id, nth_value(id, 2).partition_by(user_id)))
773 /// .load::<(String, i32, Option<i32>)>(connection)?;
774 /// let expected = vec![
775 /// ("My first post".to_owned(), 1, Some(2)),
776 /// ("About Rust".into(), 1, Some(2)),
777 /// ("My first post too".into(), 2, None),
778 /// ];
779 /// assert_eq!(expected, res);
780 /// # Ok(())
781 /// # }
782 /// ```
783 #[window]
784 fn nth_value<T: SqlType + SingleValue + IntoNullable<Nullable: SingleValue>>(
785 value: T,
786 n: Integer,
787 ) -> T::Nullable;
788}
789
790mod private {
791 pub trait SameType<T> {}
792
793 impl<T> SameType<T> for T {}
794}