Skip to content

Commit 6e598d2

Browse files
authored
Allow qualified type names in cast_as_quoted (#922)
As discussed here: #827 (comment) ## PR Info - Closes #827 - Dependencies: - #921 - #953 ## Changes The PR has been updated. The up-to-date description is in the changelog in the diff. --- **Original, outdated note:** Merge after #921. Marking as draft until then. The first commit comes as a dependency from that PR. If you want to review the diff before merging that PR, review only the second commit.
1 parent f827b75 commit 6e598d2

File tree

7 files changed

+75
-13
lines changed

7 files changed

+75
-13
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ pub struct TableName(pub Option<SchemaName>, pub DynIden);
5252

5353
* Enable `clippy::nursery` https://github.com/SeaQL/sea-query/pull/938
5454
* Removed unnecessary `'static` bounds from type signatures https://github.com/SeaQL/sea-query/pull/921
55+
* `cast_as_quoted` now allows you to [qualify the type
56+
name](https://github.com/SeaQL/sea-query/issues/827).
5557
* Most `Value` variants are now unboxed (except `BigDecimal` and `Array`). Previously the size is 24 bytes. https://github.com/SeaQL/sea-query/pull/925
5658
```rust
5759
assert_eq!(std::mem::size_of::<Value>(), 32);
@@ -96,6 +98,11 @@ assert_eq!(
9698

9799
### Breaking Changes
98100

101+
* Changed `Expr::TypeName(DynIden)` to `Expr::TypeName(TypeName)`, which can be
102+
[qualified](https://github.com/SeaQL/sea-query/issues/827).
103+
104+
If you manually construct this variant and it no longer compiles, just add
105+
`.into()`.
99106
* Removed inherent `SimpleExpr` methods that duplicate `ExprTrait`. If you encounter the following error, please add `use sea_query::ExprTrait` in scope https://github.com/SeaQL/sea-query/pull/890
100107
```rust
101108
error[E0599]: no method named `like` found for enum `sea_query::Expr` in the current scope

src/backend/query_builder.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,8 @@ pub trait QueryBuilder:
454454
Expr::Constant(val) => {
455455
self.prepare_constant(val, sql);
456456
}
457-
Expr::TypeName(iden) => {
458-
self.prepare_iden(iden, sql);
457+
Expr::TypeName(type_name) => {
458+
self.prepare_type_name(type_name, sql);
459459
}
460460
}
461461
}
@@ -910,6 +910,16 @@ pub trait QueryBuilder:
910910
self.prepare_function_name_common(function, sql)
911911
}
912912

913+
/// Translate [`TypeName`] into an SQL statement.
914+
fn prepare_type_name(&self, type_name: &TypeName, sql: &mut dyn SqlWriter) {
915+
let TypeName(schema_name, r#type) = type_name;
916+
if let Some(schema_name) = schema_name {
917+
self.prepare_schema_name(schema_name, sql);
918+
write!(sql, ".").unwrap();
919+
}
920+
self.prepare_iden(r#type, sql);
921+
}
922+
913923
/// Translate [`JoinType`] into SQL statement.
914924
fn prepare_join_type(&self, join_type: &JoinType, sql: &mut dyn SqlWriter) {
915925
self.prepare_join_type_common(join_type, sql)

src/backend/table_ref_builder.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,20 @@ pub trait TableRefBuilder: QuotedBuilder {
1919
/// Translate [`TableName`] into an SQL statement.
2020
fn prepare_table_name(&self, table_name: &TableName, sql: &mut dyn SqlWriter) {
2121
let TableName(schema_name, table) = table_name;
22-
if let Some(SchemaName(database_name, schema)) = schema_name {
23-
if let Some(DatabaseName(database)) = database_name {
24-
self.prepare_iden(database, sql);
25-
write!(sql, ".").unwrap();
26-
}
27-
self.prepare_iden(schema, sql);
22+
if let Some(schema_name) = schema_name {
23+
self.prepare_schema_name(schema_name, sql);
2824
write!(sql, ".").unwrap();
2925
}
3026
self.prepare_iden(table, sql);
3127
}
28+
29+
/// Translate [`SchemaName`] into an SQL statement.
30+
fn prepare_schema_name(&self, schema_name: &SchemaName, sql: &mut dyn SqlWriter) {
31+
let SchemaName(database_name, schema) = schema_name;
32+
if let Some(DatabaseName(database)) = database_name {
33+
self.prepare_iden(database, sql);
34+
write!(sql, ".").unwrap();
35+
}
36+
self.prepare_iden(schema, sql);
37+
}
3238
}

src/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub enum Expr {
4343
AsEnum(DynIden, Box<Expr>),
4444
Case(Box<CaseStatement>),
4545
Constant(Value),
46-
TypeName(DynIden),
46+
TypeName(TypeName),
4747
}
4848

4949
/// "Operator" methods for building expressions.

src/func.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,8 @@ impl Func {
476476

477477
/// Call `CAST` function with a case-sensitive custom type.
478478
///
479+
/// Type can be qualified with a schema name.
480+
///
479481
/// # Examples
480482
///
481483
/// ```
@@ -497,15 +499,33 @@ impl Func {
497499
/// query.to_string(SqliteQueryBuilder),
498500
/// r#"SELECT CAST('hello' AS "MyType")"#
499501
/// );
502+
///
503+
/// // Also works with a schema-qualified type name:
504+
///
505+
/// let query = Query::select()
506+
/// .expr(Func::cast_as_quoted("hello", ("MySchema", "MyType")))
507+
/// .to_owned();
508+
///
509+
/// assert_eq!(
510+
/// query.to_string(MysqlQueryBuilder),
511+
/// r#"SELECT CAST('hello' AS `MySchema`.`MyType`)"#
512+
/// );
513+
/// assert_eq!(
514+
/// query.to_string(PostgresQueryBuilder),
515+
/// r#"SELECT CAST('hello' AS "MySchema"."MyType")"#
516+
/// );
517+
/// assert_eq!(
518+
/// query.to_string(SqliteQueryBuilder),
519+
/// r#"SELECT CAST('hello' AS "MySchema"."MyType")"#
520+
/// );
500521
/// ```
501-
pub fn cast_as_quoted<V, I>(expr: V, iden: I) -> FunctionCall
522+
pub fn cast_as_quoted<V, I>(expr: V, r#type: I) -> FunctionCall
502523
where
503524
V: Into<Expr>,
504-
I: IntoIden,
525+
I: Into<TypeName>,
505526
{
506527
let expr: Expr = expr.into();
507-
FunctionCall::new(Func::Cast)
508-
.arg(expr.binary(BinOper::As, Expr::TypeName(iden.into_iden())))
528+
FunctionCall::new(Func::Cast).arg(expr.binary(BinOper::As, Expr::TypeName(r#type.into())))
509529
}
510530

511531
/// Call `COALESCE` function.

src/types/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ pub struct DatabaseName(pub DynIden);
146146
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
147147
pub struct SchemaName(pub Option<DatabaseName>, pub DynIden);
148148

149+
/// An SQL type name, potentially qualified as `(database.)(schema.)type`.
150+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
151+
pub struct TypeName(pub Option<SchemaName>, pub DynIden);
152+
149153
/// A table name, potentially qualified as `(database.)(schema.)table`.
150154
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
151155
pub struct TableName(pub Option<SchemaName>, pub DynIden);

src/types/qualification.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,21 @@ where
8787
}
8888
}
8989

90+
/// Construct a [`TypeName`] from 1-3 parts (`(database?).(schema?).type`)
91+
impl<T> From<T> for TypeName
92+
where
93+
T: MaybeQualifiedTwice,
94+
{
95+
fn from(value: T) -> Self {
96+
let (schema_parts, r#type) = value.into_3_parts();
97+
let schema_name = schema_parts.map(|schema_parts| match schema_parts {
98+
(Some(db), schema) => SchemaName(Some(DatabaseName(db)), schema),
99+
(None, schema) => SchemaName(None, schema),
100+
});
101+
TypeName(schema_name, r#type)
102+
}
103+
}
104+
90105
/// Construct a [`TableName`] from 1-3 parts (`(database?).(schema?).table`)
91106
impl<T> From<T> for TableName
92107
where

0 commit comments

Comments
 (0)