Skip to content

Commit 2593962

Browse files
authored
Merge pull request #4322 from weiznich/feature/function_expressions
Support for window functions and other aggregate function expressions
2 parents b05709e + 825c923 commit 2593962

File tree

54 files changed

+4079
-370
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+4079
-370
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ in a way that makes the pools suitable for use in parallel tests.
3434
* Support `[print_schema] allow_tables_to_appear_in_same_query_config = "none"` to generate no `allow_tables_to_appear_in_same_query!` calls. (Default: `"all_tables"`.). ([#4333](https://github.com/diesel-rs/diesel/issues/4333))
3535
* Add `[print_schema] pg_domains_as_custom_types` parameter to generate custom types for [PostgreSQL domains](https://www.postgresql.org/docs/current/domains.html) that matches any of the regexes in the given list. (Default: `[]`.) This option allows an application to selectively give special meaning for the serialization/deserialization of these types, avoiding the default behavior of treating the domain as the underlying type. ([#4592](https://github.com/diesel-rs/diesel/discussions/4592))
3636
* Add support for batch insert and upsert statements with returning for SQLite
37+
* Add support for window functions and aggregate expressions.
3738

3839
### Fixed
3940

diesel/src/backend.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,27 @@ pub trait SqlDialect: self::private::TrustedBackend {
317317
doc = "See [`sql_dialect::alias_syntax`] for provided default implementations"
318318
)]
319319
type AliasSyntax;
320+
321+
/// Configures how this backend support the `GROUP` frame unit for window functions
322+
#[cfg_attr(
323+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
324+
doc = "See [`sql_dialect::window_frame_clause_group_support`] for provided default implementations"
325+
)]
326+
type WindowFrameClauseGroupSupport;
327+
328+
/// Configures how this backend supports frame exclusion clauses
329+
#[cfg_attr(
330+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
331+
doc = "See [`sql_dialect::window_frame_exclusion_support`] for provided default implementations"
332+
)]
333+
type WindowFrameExclusionSupport;
334+
335+
/// Configures how this backend supports aggregate function expressions
336+
#[cfg_attr(
337+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
338+
doc = "See [`sql_dialect::window_frame_clause_group_support`] for provided default implementations"
339+
)]
340+
type AggregateFunctionExpressions;
320341
}
321342

322343
/// This module contains all options provided by diesel to configure the [`SqlDialect`] trait.
@@ -539,6 +560,50 @@ pub(crate) mod sql_dialect {
539560
#[derive(Debug, Copy, Clone)]
540561
pub struct AsAliasSyntax;
541562
}
563+
564+
/// This module contains all reusable options to configure [`SqlDialect::WindowFrameClauseGroupSupport`]
565+
#[diesel_derives::__diesel_public_if(
566+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
567+
)]
568+
pub mod window_frame_clause_group_support {
569+
/// Indicates that this backend does not support the `GROUPS` frame unit
570+
#[derive(Debug, Copy, Clone)]
571+
pub struct NoGroupWindowFrameUnit;
572+
573+
/// Indicates that this backend does support the `GROUPS` frame unit as specified by the standard
574+
#[derive(Debug, Copy, Clone)]
575+
pub struct IsoGroupWindowFrameUnit;
576+
}
577+
578+
/// This module contains all reusable options to configure [`SqlDialect::AggregateFunctionExpressions`]
579+
#[diesel_derives::__diesel_public_if(
580+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
581+
)]
582+
pub mod aggregate_function_expressions {
583+
/// Indicates that this backend does not support aggregate function expressions
584+
#[derive(Debug, Copy, Clone)]
585+
pub struct NoAggregateFunctionExpressions;
586+
587+
/// Indicates that this backend supports aggregate function expressions similar to PostgreSQL
588+
#[derive(Debug, Copy, Clone)]
589+
pub struct PostgresLikeAggregateFunctionExpressions;
590+
}
591+
592+
/// This module contains all reusable options to configure [`SqlDialect::WindowFrameExclusionSupport`]
593+
#[diesel_derives::__diesel_public_if(
594+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
595+
)]
596+
pub mod window_frame_exclusion_support {
597+
/// Indicates that this backend support frame exclusion clauses
598+
/// for window functions
599+
#[derive(Debug, Copy, Clone)]
600+
pub struct FrameExclusionSupport;
601+
602+
/// Indicates that this backend does not support frame exclusion clauses
603+
/// for window functions
604+
#[derive(Debug, Copy, Clone)]
605+
pub struct NoFrameFrameExclusionSupport;
606+
}
542607
}
543608

544609
// These traits are not part of the public API

diesel/src/connection/instrumentation.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,12 @@ impl DynInstrumentation {
356356
#[diesel_derives::__diesel_public_if(
357357
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
358358
)]
359+
#[cfg(any(
360+
feature = "postgres",
361+
feature = "sqlite",
362+
feature = "mysql",
363+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
364+
))]
359365
pub(crate) fn default_instrumentation() -> Self {
360366
Self {
361367
inner: get_default_instrumentation(),
@@ -367,6 +373,12 @@ impl DynInstrumentation {
367373
#[diesel_derives::__diesel_public_if(
368374
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
369375
)]
376+
#[cfg(any(
377+
feature = "postgres",
378+
feature = "sqlite",
379+
feature = "mysql",
380+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
381+
))]
370382
pub(crate) fn none() -> Self {
371383
Self {
372384
inner: None,
@@ -378,6 +390,12 @@ impl DynInstrumentation {
378390
#[diesel_derives::__diesel_public_if(
379391
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
380392
)]
393+
#[cfg(any(
394+
feature = "postgres",
395+
feature = "sqlite",
396+
feature = "mysql",
397+
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
398+
))]
381399
pub(crate) fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
382400
// This implementation is not necessary to be able to call this method on this object
383401
// because of the already existing Deref impl.

diesel/src/expression/count.rs

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::marker::PhantomData;
22

33
use super::functions::declare_sql_function;
4-
use super::{is_aggregate, AsExpression};
4+
use super::is_aggregate;
55
use super::{Expression, ValidGrouping};
66
use crate::backend::Backend;
77
use crate::query_builder::*;
@@ -17,8 +17,18 @@ extern "SQL" {
1717
/// it specifically as `diesel::dsl::count`, or glob import
1818
/// `diesel::dsl::*`
1919
///
20+
/// ## Window Function Usage
21+
///
22+
/// This function can be used as window function. See [`WindowExpressionMethods`] for details
23+
///
24+
/// ## Aggregate Function Expression
25+
///
26+
/// This function can be used as aggregate expression. See [`AggregateExpressionMethods`] for details.
27+
///
2028
/// # Examples
2129
///
30+
/// ## Normal function usage
31+
///
2232
/// ```rust
2333
/// # include!("../doctest_setup.rs");
2434
/// # use diesel::dsl::*;
@@ -29,7 +39,34 @@ extern "SQL" {
2939
/// assert_eq!(Ok(1), animals.select(count(name)).first(connection));
3040
/// # }
3141
/// ```
42+
///
43+
/// ## Window function
44+
///
45+
/// ```rust
46+
/// # include!("../doctest_setup.rs");
47+
/// # use diesel::dsl::*;
48+
/// #
49+
/// # fn main() {
50+
/// # use schema::animals::dsl::*;
51+
/// # let connection = &mut establish_connection();
52+
/// assert_eq!(Ok(1), animals.select(count(name).partition_by(id)).first(connection));
53+
/// # }
54+
/// ```
55+
///
56+
/// ## Aggregate function expression
57+
///
58+
/// ```rust
59+
/// # include!("../doctest_setup.rs");
60+
/// # use diesel::dsl::*;
61+
/// #
62+
/// # fn main() {
63+
/// # use schema::animals::dsl::*;
64+
/// # let connection = &mut establish_connection();
65+
/// assert_eq!(Ok(1), animals.select(count(name).aggregate_distinct()).first(connection));
66+
/// # }
67+
/// ```
3268
#[aggregate]
69+
#[window]
3370
fn count<T: SqlType + SingleValue>(expr: T) -> BigInt;
3471
}
3572

@@ -77,30 +114,13 @@ impl<DB: Backend> QueryFragment<DB> for CountStar {
77114

78115
impl_selectable_expression!(CountStar);
79116

80-
/// Creates a SQL `COUNT(DISTINCT ...)` expression
81-
///
82-
/// As with most bare functions, this is not exported by default. You can import
83-
/// it specifically as `diesel::dsl::count_distinct`, or glob import
84-
/// `diesel::dsl::*`
85-
///
86-
/// # Examples
87-
///
88-
/// ```rust
89-
/// # #[macro_use] extern crate diesel;
90-
/// # include!("../doctest_setup.rs");
91-
/// # use diesel::dsl::*;
92-
/// #
93-
/// # fn main() {
94-
/// # use schema::posts::dsl::*;
95-
/// # let connection = &mut establish_connection();
96-
/// let unique_user_count = posts.select(count_distinct(user_id)).first(connection);
97-
/// assert_eq!(Ok(2), unique_user_count);
98-
/// # }
99-
/// ```
117+
#[doc(hidden)]
118+
#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
119+
#[deprecated(note = "Use `AggregateExpressionMethods::aggregate_distinct` instead")]
100120
pub fn count_distinct<T, E>(expr: E) -> CountDistinct<T, E::Expression>
101121
where
102122
T: SqlType + SingleValue,
103-
E: AsExpression<T>,
123+
E: crate::expression::AsExpression<T>,
104124
{
105125
CountDistinct {
106126
expr: expr.as_expression(),

0 commit comments

Comments
 (0)