Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 101 additions & 45 deletions src/query/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,82 @@ where
}
}

impl From<WindowStatement> for WindowSelectType {
fn from(stmt: WindowStatement) -> Self {
Self::Query(stmt)
}
}

impl<T: IntoIden> From<T> for WindowSelectType {
fn from(iden: T) -> Self {
Self::Name(iden.into_iden())
}
}

/// Extension methods for building a [`SelectExpr`] from an expression.
///
/// This makes it ergonomic to attach select-specific modifiers (like `AS` and `OVER`) and pass the
/// result into [`SelectStatement::expr`].
///
/// # Examples
///
/// ```
/// use sea_query::{tests_cfg::*, *};
///
/// let query = Query::select()
/// .from(Char::Table)
/// .expr(
/// Expr::col(Char::Character)
/// .max()
/// .over(WindowStatement::partition_by(Char::FontSize))
/// .alias("C"),
/// )
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT MAX(`character`) OVER ( PARTITION BY `font_size` ) AS `C` FROM `character`"#
/// );
/// ```
pub trait SelectExprTrait: Sized {
fn alias<A>(self, alias: A) -> SelectExpr
where
A: IntoIden;

fn over(self, over_expr: impl Into<WindowSelectType>) -> SelectExpr;
}

impl SelectExprTrait for SelectExpr {
fn alias<A>(mut self, alias: A) -> SelectExpr
where
A: IntoIden,
{
self.alias = Some(alias.into_iden());
self
}

fn over(mut self, over_expr: impl Into<WindowSelectType>) -> SelectExpr {
self.window = Some(over_expr.into());
self
}
}

impl<T> SelectExprTrait for T
where
T: Into<Expr>,
{
fn alias<A>(self, alias: A) -> SelectExpr
where
A: IntoIden,
{
SelectExpr::from(self).alias(alias)
}

fn over(self, over_expr: impl Into<WindowSelectType>) -> SelectExpr {
SelectExpr::from(self).over(over_expr)
}
}

impl SelectStatement {
/// Construct a new [`SelectStatement`]
pub fn new() -> Self {
Expand Down Expand Up @@ -678,11 +754,7 @@ impl SelectStatement {
T: Into<Expr>,
A: IntoIden,
{
self.expr(SelectExpr {
expr: expr.into(),
alias: Some(alias.into_iden()),
window: None,
});
self.expr(expr.alias(alias));
self
}

Expand All @@ -696,33 +768,29 @@ impl SelectStatement {
/// let query = Query::select()
/// .from(Char::Table)
/// .expr_window(
/// Expr::col(Char::Character),
/// Expr::col(Char::Character).max(),
/// WindowStatement::partition_by(Char::FontSize),
/// )
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT `character` OVER ( PARTITION BY `font_size` ) FROM `character`"#
/// r#"SELECT MAX(`character`) OVER ( PARTITION BY `font_size` ) FROM `character`"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "character" OVER ( PARTITION BY "font_size" ) FROM "character""#
/// r#"SELECT MAX("character") OVER ( PARTITION BY "font_size" ) FROM "character""#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT "character" OVER ( PARTITION BY "font_size" ) FROM "character""#
/// r#"SELECT MAX("character") OVER ( PARTITION BY "font_size" ) FROM "character""#
/// );
/// ```
pub fn expr_window<T>(&mut self, expr: T, window: WindowStatement) -> &mut Self
where
T: Into<Expr>,
{
self.expr(SelectExpr {
expr: expr.into(),
alias: None,
window: Some(WindowSelectType::Query(window)),
});
self.expr(expr.over(window));
self
}

Expand All @@ -736,35 +804,31 @@ impl SelectStatement {
/// let query = Query::select()
/// .from(Char::Table)
/// .expr_window_as(
/// Expr::col(Char::Character),
/// Expr::col(Char::Character).max(),
/// WindowStatement::partition_by(Char::FontSize),
/// "C",
/// )
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT `character` OVER ( PARTITION BY `font_size` ) AS `C` FROM `character`"#
/// r#"SELECT MAX(`character`) OVER ( PARTITION BY `font_size` ) AS `C` FROM `character`"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "character" OVER ( PARTITION BY "font_size" ) AS "C" FROM "character""#
/// r#"SELECT MAX("character") OVER ( PARTITION BY "font_size" ) AS "C" FROM "character""#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT "character" OVER ( PARTITION BY "font_size" ) AS "C" FROM "character""#
/// r#"SELECT MAX("character") OVER ( PARTITION BY "font_size" ) AS "C" FROM "character""#
/// );
/// ```
pub fn expr_window_as<T, A>(&mut self, expr: T, window: WindowStatement, alias: A) -> &mut Self
where
T: Into<Expr>,
A: IntoIden,
{
self.expr(SelectExpr {
expr: expr.into(),
alias: Some(alias.into_iden()),
window: Some(WindowSelectType::Query(window)),
});
self.expr(expr.over(window).alias(alias));
self
}

Expand All @@ -777,33 +841,29 @@ impl SelectStatement {
///
/// let query = Query::select()
/// .from(Char::Table)
/// .expr_window_name(Expr::col(Char::Character), "w")
/// .expr_window_name(Expr::col(Char::Character).max(), "w")
/// .window("w", WindowStatement::partition_by(Char::FontSize))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT `character` OVER `w` FROM `character` WINDOW `w` AS (PARTITION BY `font_size`)"#
/// r#"SELECT MAX(`character`) OVER `w` FROM `character` WINDOW `w` AS (PARTITION BY `font_size`)"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "character" OVER "w" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// r#"SELECT MAX("character") OVER "w" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT "character" OVER "w" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// r#"SELECT MAX("character") OVER "w" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// );
/// ```
pub fn expr_window_name<T, W>(&mut self, expr: T, window: W) -> &mut Self
where
T: Into<Expr>,
W: IntoIden,
{
self.expr(SelectExpr {
expr: expr.into(),
alias: None,
window: Some(WindowSelectType::Name(window.into_iden())),
});
self.expr(expr.over(window));
self
}

Expand All @@ -816,21 +876,21 @@ impl SelectStatement {
///
/// let query = Query::select()
/// .from(Char::Table)
/// .expr_window_name_as(Expr::col(Char::Character), "w", "C")
/// .expr_window_name_as(Expr::col(Char::Character).max(), "w", "C")
/// .window("w", WindowStatement::partition_by(Char::FontSize))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT `character` OVER `w` AS `C` FROM `character` WINDOW `w` AS (PARTITION BY `font_size`)"#
/// r#"SELECT MAX(`character`) OVER `w` AS `C` FROM `character` WINDOW `w` AS (PARTITION BY `font_size`)"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "character" OVER "w" AS "C" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// r#"SELECT MAX("character") OVER "w" AS "C" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT "character" OVER "w" AS "C" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// r#"SELECT MAX("character") OVER "w" AS "C" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// );
/// ```
pub fn expr_window_name_as<T, W, A>(&mut self, expr: T, window: W, alias: A) -> &mut Self
Expand All @@ -839,11 +899,7 @@ impl SelectStatement {
A: IntoIden,
W: IntoIden,
{
self.expr(SelectExpr {
expr: expr.into(),
alias: Some(alias.into_iden()),
window: Some(WindowSelectType::Name(window.into_iden())),
});
self.expr(expr.over(window).alias(alias));
self
}

Expand Down Expand Up @@ -2549,21 +2605,21 @@ impl SelectStatement {
///
/// let query = Query::select()
/// .from(Char::Table)
/// .expr_window_name_as(Expr::col(Char::Character), "w", "C")
/// .expr_window_name_as(Expr::col(Char::Character).max(), "w", "C")
/// .window("w", WindowStatement::partition_by(Char::FontSize))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT `character` OVER `w` AS `C` FROM `character` WINDOW `w` AS (PARTITION BY `font_size`)"#
/// r#"SELECT MAX(`character`) OVER `w` AS `C` FROM `character` WINDOW `w` AS (PARTITION BY `font_size`)"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "character" OVER "w" AS "C" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// r#"SELECT MAX("character") OVER "w" AS "C" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT "character" OVER "w" AS "C" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// r#"SELECT MAX("character") OVER "w" AS "C" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
/// );
/// ```
pub fn window<A>(&mut self, name: A, window: WindowStatement) -> &mut Self
Expand Down
37 changes: 37 additions & 0 deletions tests/mysql/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,15 @@ fn select_32() {
.to_string(MysqlQueryBuilder),
"SELECT `character` AS `C` FROM `character`"
);

// Same SQL as `expr_as`, but expressed via `SelectExprTrait`.
assert_eq!(
Query::select()
.expr(Expr::col(Char::Character).alias("C"))
.from(Char::Table)
.to_string(MysqlQueryBuilder),
"SELECT `character` AS `C` FROM `character`"
);
}

#[test]
Expand Down Expand Up @@ -1042,6 +1051,34 @@ fn select_61() {
);
}

#[test]
fn select_62() {
assert_eq!(
Query::select()
.from(Char::Table)
.expr(
Expr::col(Char::Character)
.max()
.over(WindowStatement::partition_by(Char::FontSize))
.alias("C"),
)
.to_string(MysqlQueryBuilder),
r#"SELECT MAX(`character`) OVER ( PARTITION BY `font_size` ) AS `C` FROM `character`"#
);
}

#[test]
fn select_63() {
assert_eq!(
Query::select()
.from(Char::Table)
.expr(Expr::col(Char::Character).max().over("w"))
.window("w", WindowStatement::partition_by(Char::FontSize))
.to_string(MysqlQueryBuilder),
r#"SELECT MAX(`character`) OVER `w` FROM `character` WINDOW `w` AS (PARTITION BY `font_size`)"#
);
}

#[test]
fn md5_fn() {
assert_eq!(
Expand Down
37 changes: 37 additions & 0 deletions tests/postgres/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,15 @@ fn select_32() {
query.audit_unwrap().selected_tables(),
[Char::Table.into_iden()]
);

// Same SQL as `expr_as`, but expressed via `SelectExprTrait`.
assert_eq!(
Query::select()
.expr(Expr::col(Char::Character).alias("C"))
.from(Char::Table)
.to_string(PostgresQueryBuilder),
r#"SELECT "character" AS "C" FROM "character""#
);
}

#[test]
Expand Down Expand Up @@ -1262,6 +1271,34 @@ fn select_64() {
assert_eq!(query.audit_unwrap().selected_tables(), []);
}

#[test]
fn select_65() {
assert_eq!(
Query::select()
.from(Char::Table)
.expr(
Expr::col(Char::Character)
.max()
.over(WindowStatement::partition_by(Char::FontSize))
.alias("C"),
)
.to_string(PostgresQueryBuilder),
r#"SELECT MAX("character") OVER ( PARTITION BY "font_size" ) AS "C" FROM "character""#
);
}

#[test]
fn select_66() {
assert_eq!(
Query::select()
.from(Char::Table)
.expr(Expr::col(Char::Character).max().over("w"))
.window("w", WindowStatement::partition_by(Char::FontSize))
.to_string(PostgresQueryBuilder),
r#"SELECT MAX("character") OVER "w" FROM "character" WINDOW "w" AS (PARTITION BY "font_size")"#
);
}

#[test]
#[allow(clippy::approx_constant)]
fn insert_2() {
Expand Down
Loading
Loading