Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feat/update-from' into feat/upda…
Browse files Browse the repository at this point in the history
…te-from
  • Loading branch information
the-wondersmith committed Dec 29, 2024
2 parents 854bec4 + 73b069e commit 2df8b6b
Show file tree
Hide file tree
Showing 13 changed files with 479 additions and 120 deletions.
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## 0.32.1 - 2024-12-01

### New Features

* Added `Value::as_null`
```rust
let v = Value::Int(Some(2));
let n = v.as_null();

assert_eq!(n, Value::Int(None));
```
* Added bitwise and/or operators (`bit_and`, `bit_or`) https://github.com/SeaQL/sea-query/pull/841
```rust
let query = Query::select()
.expr(1.bit_and(2).eq(3))
.to_owned();

assert_eq!(
query.to_string(PostgresQueryBuilder),
r#"SELECT (1 & 2) = 3"#
);
```

### Enhancements

* Added `GREATEST` & `LEAST` function https://github.com/SeaQL/sea-query/pull/844
* Added `ValueType::enum_type_name()` https://github.com/SeaQL/sea-query/pull/836
* Removed "one common table" restriction on recursive CTE https://github.com/SeaQL/sea-query/pull/835

### House keeping

* Remove unnecessary string hashes https://github.com/SeaQL/sea-query/pull/815

## 0.32.0 - 2024-10-17

### Releases
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = [".", "sea-query-derive"]

[package]
name = "sea-query"
version = "0.32.0"
version = "0.32.1"
authors = [
"Chris Tsang <chris.2y3@outlook.com>",
"Billy Chan <ccw.billy.123@gmail.com>",
Expand Down
34 changes: 19 additions & 15 deletions src/backend/query_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,12 +483,9 @@ pub trait QueryBuilder:
None => {}
};

match &select_expr.alias {
Some(alias) => {
write!(sql, " AS ").unwrap();
alias.prepare(sql.as_writer(), self.quote());
}
None => {}
if let Some(alias) = &select_expr.alias {
write!(sql, " AS ").unwrap();
alias.prepare(sql.as_writer(), self.quote());
};
}

Expand Down Expand Up @@ -604,6 +601,8 @@ pub trait QueryBuilder:
BinOper::As => "AS",
BinOper::Escape => "ESCAPE",
BinOper::Custom(raw) => raw,
BinOper::BitAnd => "&",
BinOper::BitOr => "|",
#[allow(unreachable_patterns)]
_ => unimplemented!(),
}
Expand Down Expand Up @@ -679,6 +678,8 @@ pub trait QueryBuilder:
Function::Coalesce => "COALESCE",
Function::Count => "COUNT",
Function::IfNull => self.if_null_function(),
Function::Greatest => self.greatest_function(),
Function::Least => self.least_function(),
Function::CharLength => self.char_length_function(),
Function::Cast => "CAST",
Function::Lower => "LOWER",
Expand Down Expand Up @@ -789,15 +790,6 @@ pub trait QueryBuilder:
"Cannot build a with query that has no common table expression!"
);

if with_clause.recursive {
assert_eq!(
with_clause.cte_expressions.len(),
1,
"Cannot build a recursive query with more than one common table! \
A recursive with query must have a single cte inside it that has a union query of \
two queries!"
);
}
for cte in &with_clause.cte_expressions {
if !cte_first {
write!(sql, ", ").unwrap();
Expand Down Expand Up @@ -1481,6 +1473,18 @@ pub trait QueryBuilder:
"IFNULL"
}

#[doc(hidden)]
/// The name of the function that represents the "greatest" function.
fn greatest_function(&self) -> &str {
"GREATEST"
}

#[doc(hidden)]
/// The name of the function that represents the "least" function.
fn least_function(&self) -> &str {
"LEAST"
}

#[doc(hidden)]
/// The name of the function that returns the char length.
fn char_length_function(&self) -> &str {
Expand Down
8 changes: 8 additions & 0 deletions src/backend/sqlite/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ impl QueryBuilder for SqliteQueryBuilder {
sql.push_param(value.clone(), self as _);
}

fn greatest_function(&self) -> &str {
"MAX"
}

fn least_function(&self) -> &str {
"MIN"
}

fn char_length_function(&self) -> &str {
"LENGTH"
}
Expand Down
73 changes: 73 additions & 0 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,79 @@ pub trait ExprTrait: Sized {
/// );
/// ```
fn unary(self, o: UnOper) -> SimpleExpr;

/// Express a bitwise AND operation.
///
/// # Examples
///
/// ```
/// use sea_query::{tests_cfg::*, *};
///
/// let query = Query::select().expr(1.bit_and(2).eq(3)).to_owned();
///
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT (1 & 2) = 3"#
/// );
///
/// let query = Query::select()
/// .columns([Char::Character, Char::SizeW, Char::SizeH])
/// .from(Char::Table)
/// .and_where(1.bit_and(1).eq(1))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE (1 & 1) = 1"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE (1 & 1) = 1"#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE (1 & 1) = 1"#
/// );
/// ```
fn bit_and<R>(self, right: R) -> SimpleExpr
where
R: Into<SimpleExpr>,
{
ExprTrait::binary(self, BinOper::BitAnd, right)
}

/// Express a bitwise OR operation.
///
/// # Examples
///
/// ```
/// use sea_query::{tests_cfg::*, *};
///
/// let query = Query::select()
/// .columns([Char::Character, Char::SizeW, Char::SizeH])
/// .from(Char::Table)
/// .and_where(1.bit_or(1).eq(1))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT `character`, `size_w`, `size_h` FROM `character` WHERE (1 | 1) = 1"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE (1 | 1) = 1"#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE (1 | 1) = 1"#
/// );
/// ```
fn bit_or<R>(self, right: R) -> SimpleExpr
where
R: Into<SimpleExpr>,
{
ExprTrait::binary(self, BinOper::BitOr, right)
}
}

/// This generic implementation covers all expression types,
Expand Down
72 changes: 72 additions & 0 deletions src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub enum Function {
Abs,
Count,
IfNull,
Greatest,
Least,
CharLength,
Cast,
Custom(DynIden),
Expand Down Expand Up @@ -392,6 +394,76 @@ impl Func {
FunctionCall::new(Function::CharLength).arg(expr)
}

/// Call `GREATEST` function.
///
/// # Examples
///
/// ```
/// use sea_query::{tests_cfg::*, *};
///
/// let query = Query::select()
/// .expr(Func::greatest([
/// Expr::col(Char::SizeW).into(),
/// Expr::col(Char::SizeH).into(),
/// ]))
/// .from(Char::Table)
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT GREATEST(`size_w`, `size_h`) FROM `character`"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT GREATEST("size_w", "size_h") FROM "character""#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT MAX("size_w", "size_h") FROM "character""#
/// );
/// ```
pub fn greatest<I>(args: I) -> FunctionCall
where
I: IntoIterator<Item = SimpleExpr>,
{
FunctionCall::new(Function::Greatest).args(args)
}

/// Call `LEAST` function.
///
/// # Examples
///
/// ```
/// use sea_query::{tests_cfg::*, *};
///
/// let query = Query::select()
/// .expr(Func::least([
/// Expr::col(Char::SizeW).into(),
/// Expr::col(Char::SizeH).into(),
/// ]))
/// .from(Char::Table)
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"SELECT LEAST(`size_w`, `size_h`) FROM `character`"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT LEAST("size_w", "size_h") FROM "character""#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"SELECT MIN("size_w", "size_h") FROM "character""#
/// );
/// ```
pub fn least<I>(args: I) -> FunctionCall
where
I: IntoIterator<Item = SimpleExpr>,
{
FunctionCall::new(Function::Least).args(args)
}

/// Call `IF NULL` function.
///
/// # Examples
Expand Down
18 changes: 18 additions & 0 deletions src/query/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,24 @@ impl UpdateStatement {
/// query.to_string(SqliteQueryBuilder),
/// r#"UPDATE "glyph" SET "aspect" = 60 * 24 * 24, "image" = '24B0E11951B03B07F8300FD003983F03F0780060'"#
/// );
///
/// let query = Query::update()
/// .table(Glyph::Table)
/// .value(Glyph::Aspect, Expr::value(Value::Int(None)))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(MysqlQueryBuilder),
/// r#"UPDATE `glyph` SET `aspect` = NULL"#
/// );
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"UPDATE "glyph" SET "aspect" = NULL"#
/// );
/// assert_eq!(
/// query.to_string(SqliteQueryBuilder),
/// r#"UPDATE "glyph" SET "aspect" = NULL"#
/// );
/// ```
pub fn value<C, T>(&mut self, col: C, value: T) -> &mut Self
where
Expand Down
10 changes: 5 additions & 5 deletions src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ mod tests {

#[test]
fn test_9() {
let string = r#"[ab] "#;
let string = r"[ab] ";
let tokenizer = Tokenizer::new(string);
let tokens: Vec<Token> = tokenizer.iter().collect();
assert_eq!(
Expand Down Expand Up @@ -491,7 +491,7 @@ mod tests {

#[test]
fn test_11() {
let string = r#" `a``b` "#;
let string = r" `a``b` ";
let tokenizer = Tokenizer::new(string);
let tokens: Vec<Token> = tokenizer.iter().collect();
assert_eq!(
Expand All @@ -510,7 +510,7 @@ mod tests {

#[test]
fn test_12() {
let string = r#" 'a''b' "#;
let string = r" 'a''b' ";
let tokenizer = Tokenizer::new(string);
let tokens: Vec<Token> = tokenizer.iter().collect();
assert_eq!(
Expand All @@ -529,7 +529,7 @@ mod tests {

#[test]
fn test_13() {
let string = r#"(?)"#;
let string = r"(?)";
let tokenizer = Tokenizer::new(string);
let tokens: Vec<Token> = tokenizer.iter().collect();
assert_eq!(
Expand All @@ -548,7 +548,7 @@ mod tests {

#[test]
fn test_14() {
let string = r#"($1 = $2)"#;
let string = r"($1 = $2)";
let tokenizer = Tokenizer::new(string);
let tokens: Vec<Token> = tokenizer.iter().collect();
assert_eq!(
Expand Down
Loading

0 comments on commit 2df8b6b

Please sign in to comment.