use crate::dialect::{MySQLDialect, PostgresDialect, SQLiteDialect};
use crate::sql::{SQL, Token};
use crate::traits::{SQLColumnInfo, SQLParam, ToSQL};
use crate::types::{Compatible, DataType, Textual};
use super::{AggOr, AggregateKind, Expr, NonNull, Null, NullOr, Nullability, SQLExpr, Scalar};
#[derive(Clone, Copy, Debug)]
pub struct AliasedExpr<E> {
pub(crate) expr: E,
pub(crate) name: &'static str,
}
impl<'a, V, E> ToSQL<'a, V> for AliasedExpr<E>
where
V: SQLParam + 'a,
E: ToSQL<'a, V>,
{
fn to_sql(&self) -> SQL<'a, V> {
self.expr.to_sql().alias(self.name)
}
fn into_sql(self) -> SQL<'a, V> {
self.expr.into_sql().alias(self.name)
}
}
impl<'a, V, E> Expr<'a, V> for AliasedExpr<E>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
{
type SQLType = E::SQLType;
type Nullable = E::Nullable;
type Aggregate = E::Aggregate;
fn to_expr_sql(&self) -> SQL<'a, V> {
self.expr.to_expr_sql().alias(self.name)
}
fn into_expr_sql(self) -> SQL<'a, V> {
self.expr.into_expr_sql().alias(self.name)
}
}
impl<E: super::HasAggStatus> super::HasAggStatus for AliasedExpr<E> {
type Status = E::Status;
}
impl<E: crate::row::ExprValueType> crate::row::ExprValueType for AliasedExpr<E> {
type ValueType = E::ValueType;
}
impl<E> crate::row::IntoSelectTarget for AliasedExpr<E>
where
E: crate::row::ExprValueType,
{
type Marker = crate::row::SelectCols<(Self,)>;
}
pub trait AliasExt: Sized {
fn alias(self, name: &'static str) -> AliasedExpr<Self> {
AliasedExpr { expr: self, name }
}
}
impl<T: Sized> AliasExt for T {}
pub const fn alias<E>(expr: E, name: &'static str) -> AliasedExpr<E> {
AliasedExpr { expr, name }
}
#[derive(Clone, Copy, Debug)]
pub struct NamedExpr<E, Name> {
pub(crate) expr: E,
pub(crate) name: core::marker::PhantomData<Name>,
}
impl<E, Name> NamedExpr<E, Name> {
pub const fn expression(&self) -> &E {
&self.expr
}
pub fn into_expression(self) -> E {
self.expr
}
}
impl<'a, V, E, Name> ToSQL<'a, V> for NamedExpr<E, Name>
where
V: SQLParam + 'a,
E: ToSQL<'a, V>,
Name: crate::Tag,
{
fn to_sql(&self) -> SQL<'a, V> {
self.expr.to_sql().alias(Name::NAME)
}
fn into_sql(self) -> SQL<'a, V> {
self.expr.into_sql().alias(Name::NAME)
}
}
impl<'a, V, E, Name> Expr<'a, V> for NamedExpr<E, Name>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
Name: crate::Tag,
{
type SQLType = E::SQLType;
type Nullable = E::Nullable;
type Aggregate = E::Aggregate;
fn to_expr_sql(&self) -> SQL<'a, V> {
self.expr.to_expr_sql().alias(Name::NAME)
}
fn into_expr_sql(self) -> SQL<'a, V> {
self.expr.into_expr_sql().alias(Name::NAME)
}
}
impl<E, Name> super::HasAggStatus for NamedExpr<E, Name>
where
E: super::HasAggStatus,
{
type Status = E::Status;
}
impl<E, Name> crate::row::ExprValueType for NamedExpr<E, Name>
where
E: crate::row::ExprValueType,
{
type ValueType = E::ValueType;
}
impl<E, Name> crate::row::IntoSelectTarget for NamedExpr<E, Name>
where
E: crate::row::ExprValueType,
{
type Marker = crate::row::SelectCols<(Self,)>;
}
impl<E, Name> crate::row::GroupByIdentity for NamedExpr<E, Name>
where
E: crate::row::GroupByIdentity,
{
type Identity = E::Identity;
}
pub trait NamedExt: Sized {
fn named<Name: crate::Tag>(self) -> NamedExpr<Self, Name> {
NamedExpr {
expr: self,
name: core::marker::PhantomData,
}
}
}
impl<T: crate::row::ExprValueType> NamedExt for T {}
#[diagnostic::on_unimplemented(
message = "TYPEOF is not available for this dialect",
label = "use a dialect-specific type inspection expression"
)]
pub trait TypeofSupport {}
impl TypeofSupport for SQLiteDialect {}
pub fn typeof_<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as crate::dialect::DialectTypes>::Text, NonNull, E::Aggregate>
where
V: SQLParam + 'a,
V::DialectMarker: TypeofSupport,
E: Expr<'a, V>,
{
SQLExpr::new(SQL::func("TYPEOF", expr.into_expr_sql()))
}
pub fn r#typeof<'a, V, E>(
expr: E,
) -> SQLExpr<'a, V, <V::DialectMarker as crate::dialect::DialectTypes>::Text, NonNull, E::Aggregate>
where
V: SQLParam + 'a,
V::DialectMarker: TypeofSupport,
E: Expr<'a, V>,
{
typeof_(expr)
}
pub trait DefaultCastTypeName: DataType {
const CAST_TYPE_NAME: &'static str;
}
impl DefaultCastTypeName for drizzle_types::sqlite::types::Integer {
const CAST_TYPE_NAME: &'static str = "INTEGER";
}
impl DefaultCastTypeName for drizzle_types::sqlite::types::Text {
const CAST_TYPE_NAME: &'static str = "TEXT";
}
impl DefaultCastTypeName for drizzle_types::sqlite::types::Real {
const CAST_TYPE_NAME: &'static str = "REAL";
}
impl DefaultCastTypeName for drizzle_types::sqlite::types::Blob {
const CAST_TYPE_NAME: &'static str = "BLOB";
}
impl DefaultCastTypeName for drizzle_types::sqlite::types::Numeric {
const CAST_TYPE_NAME: &'static str = "NUMERIC";
}
impl DefaultCastTypeName for drizzle_types::sqlite::types::Any {
const CAST_TYPE_NAME: &'static str = "ANY";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Int2 {
const CAST_TYPE_NAME: &'static str = "SMALLINT";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Int4 {
const CAST_TYPE_NAME: &'static str = "INTEGER";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Int8 {
const CAST_TYPE_NAME: &'static str = "BIGINT";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Float4 {
const CAST_TYPE_NAME: &'static str = "REAL";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Float8 {
const CAST_TYPE_NAME: &'static str = "DOUBLE PRECISION";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Varchar {
const CAST_TYPE_NAME: &'static str = "VARCHAR";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Text {
const CAST_TYPE_NAME: &'static str = "TEXT";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Char {
const CAST_TYPE_NAME: &'static str = "CHAR";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Bytea {
const CAST_TYPE_NAME: &'static str = "BYTEA";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Boolean {
const CAST_TYPE_NAME: &'static str = "BOOLEAN";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Timestamptz {
const CAST_TYPE_NAME: &'static str = "TIMESTAMPTZ";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Timestamp {
const CAST_TYPE_NAME: &'static str = "TIMESTAMP";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Date {
const CAST_TYPE_NAME: &'static str = "DATE";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Time {
const CAST_TYPE_NAME: &'static str = "TIME";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Timetz {
const CAST_TYPE_NAME: &'static str = "TIMETZ";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Numeric {
const CAST_TYPE_NAME: &'static str = "NUMERIC";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Uuid {
const CAST_TYPE_NAME: &'static str = "UUID";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Json {
const CAST_TYPE_NAME: &'static str = "JSON";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Jsonb {
const CAST_TYPE_NAME: &'static str = "JSONB";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Any {
const CAST_TYPE_NAME: &'static str = "ANY";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Interval {
const CAST_TYPE_NAME: &'static str = "INTERVAL";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Inet {
const CAST_TYPE_NAME: &'static str = "INET";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Cidr {
const CAST_TYPE_NAME: &'static str = "CIDR";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::MacAddr {
const CAST_TYPE_NAME: &'static str = "MACADDR";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::MacAddr8 {
const CAST_TYPE_NAME: &'static str = "MACADDR8";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Point {
const CAST_TYPE_NAME: &'static str = "POINT";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::LineString {
const CAST_TYPE_NAME: &'static str = "PATH";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Rect {
const CAST_TYPE_NAME: &'static str = "BOX";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::BitString {
const CAST_TYPE_NAME: &'static str = "BIT VARYING";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Line {
const CAST_TYPE_NAME: &'static str = "LINE";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::LineSegment {
const CAST_TYPE_NAME: &'static str = "LSEG";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Polygon {
const CAST_TYPE_NAME: &'static str = "POLYGON";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Circle {
const CAST_TYPE_NAME: &'static str = "CIRCLE";
}
impl DefaultCastTypeName for drizzle_types::postgres::types::Enum {
const CAST_TYPE_NAME: &'static str = "TEXT";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::BigInt {
const CAST_TYPE_NAME: &'static str = "SIGNED";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::BigIntUnsigned {
const CAST_TYPE_NAME: &'static str = "UNSIGNED";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Float {
const CAST_TYPE_NAME: &'static str = "FLOAT";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Double {
const CAST_TYPE_NAME: &'static str = "DOUBLE";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Decimal {
const CAST_TYPE_NAME: &'static str = "DECIMAL";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Varchar {
const CAST_TYPE_NAME: &'static str = "CHAR";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Varbinary {
const CAST_TYPE_NAME: &'static str = "BINARY";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Json {
const CAST_TYPE_NAME: &'static str = "JSON";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Date {
const CAST_TYPE_NAME: &'static str = "DATE";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Time {
const CAST_TYPE_NAME: &'static str = "TIME";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::DateTime {
const CAST_TYPE_NAME: &'static str = "DATETIME";
}
impl DefaultCastTypeName for drizzle_types::mysql::types::Year {
const CAST_TYPE_NAME: &'static str = "YEAR";
}
pub trait CastTarget<'a, T: DataType, D> {
fn cast_type_name(self) -> &'a str;
}
#[diagnostic::on_unimplemented(
message = "cannot cast `{Source}` to `{Target}` for this dialect",
label = "cast target is incompatible with source type",
note = "use a supported target marker, or raw SQL when the conversion is intentionally dialect-specific"
)]
pub trait CastTypePolicy<D, Source: DataType, Target: DataType> {}
#[doc(hidden)]
pub trait CastNullabilityPolicy<D, Input: Nullability>: DataType {
type Output: Nullability;
}
macro_rules! mysql_cast_policy {
(
preserving: [$($preserving:ty),+ $(,)?],
nullable: [$($nullable:ty),+ $(,)?],
) => {
$(
impl<Source: DataType> CastTypePolicy<MySQLDialect, Source, $preserving> for () {}
impl<Input: Nullability> CastNullabilityPolicy<MySQLDialect, Input> for $preserving {
type Output = Input;
}
)+
$(
impl<Source: DataType> CastTypePolicy<MySQLDialect, Source, $nullable> for () {}
impl<Input: Nullability> CastNullabilityPolicy<MySQLDialect, Input> for $nullable {
type Output = Null;
}
)+
};
}
mysql_cast_policy! {
preserving: [
drizzle_types::mysql::types::BigInt,
drizzle_types::mysql::types::BigIntUnsigned,
drizzle_types::mysql::types::Float,
drizzle_types::mysql::types::Double,
drizzle_types::mysql::types::Decimal,
drizzle_types::mysql::types::Varchar,
drizzle_types::mysql::types::Varbinary,
drizzle_types::mysql::types::Json,
],
nullable: [
drizzle_types::mysql::types::Date,
drizzle_types::mysql::types::Time,
drizzle_types::mysql::types::DateTime,
drizzle_types::mysql::types::Year,
],
}
impl<Source: DataType + Compatible<Target>, Target: DataType>
CastTypePolicy<PostgresDialect, Source, Target> for ()
{
}
impl<Input: Nullability, Target: DataType> CastNullabilityPolicy<PostgresDialect, Input>
for Target
{
type Output = Input;
}
impl<Source: DataType + Compatible<Target>, Target: DataType>
CastTypePolicy<SQLiteDialect, Source, Target> for ()
{
}
impl<Input: Nullability, Target: DataType> CastNullabilityPolicy<SQLiteDialect, Input> for Target {
type Output = Input;
}
impl<'a, T: DataType, D> CastTarget<'a, T, D> for &'a str {
fn cast_type_name(self) -> &'a str {
self
}
}
impl<'a, T, D> CastTarget<'a, T, D> for T
where
T: DataType + DefaultCastTypeName,
{
fn cast_type_name(self) -> &'a str {
T::CAST_TYPE_NAME
}
}
#[allow(clippy::type_complexity)]
pub fn cast<'a, V, E, Target>(
expr: E,
target_type: impl CastTarget<'a, Target, V::DialectMarker>,
) -> SQLExpr<
'a,
V,
Target,
<Target as CastNullabilityPolicy<V::DialectMarker, E::Nullable>>::Output,
E::Aggregate,
>
where
V: SQLParam + 'a,
E: Expr<'a, V>,
Target: DataType + CastNullabilityPolicy<V::DialectMarker, E::Nullable>,
(): CastTypePolicy<V::DialectMarker, E::SQLType, Target>,
{
SQLExpr::new(SQL::func(
"CAST",
expr.into_expr_sql()
.push(Token::AS)
.append(SQL::raw(target_type.cast_type_name())),
))
}
#[allow(clippy::type_complexity)]
pub fn string_concat<'a, V, L, R>(
left: L,
right: R,
) -> SQLExpr<
'a,
V,
<V::DialectMarker as crate::dialect::DialectTypes>::Text,
<L::Nullable as NullOr<R::Nullable>>::Output,
<L::Aggregate as AggOr<R::Aggregate>>::Output,
>
where
V: SQLParam + 'a,
L: Expr<'a, V>,
R: Expr<'a, V>,
L::SQLType: Textual,
R::SQLType: Textual,
L::Nullable: NullOr<R::Nullable>,
R::Nullable: Nullability,
L::Aggregate: AggOr<R::Aggregate>,
R::Aggregate: AggregateKind,
{
super::concat(left, right)
}
#[must_use]
pub fn raw<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, Null, Scalar>
where
V: SQLParam + 'a,
T: DataType,
{
SQLExpr::new(SQL::raw(sql))
}
#[must_use]
pub fn raw_nullable<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, Null, Scalar>
where
V: SQLParam + 'a,
T: DataType,
{
SQLExpr::new(SQL::raw(sql))
}
#[must_use]
pub fn raw_non_null<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, NonNull, Scalar>
where
V: SQLParam + 'a,
T: DataType,
{
SQLExpr::new(SQL::raw(sql))
}
#[derive(Clone, Copy, Debug)]
pub struct Excluded<C> {
column: C,
}
pub trait ExcludedSupport {}
impl ExcludedSupport for SQLiteDialect {}
impl ExcludedSupport for PostgresDialect {}
pub const fn excluded<C>(column: C) -> Excluded<C> {
Excluded { column }
}
impl<'a, V, C> Expr<'a, V> for Excluded<C>
where
V: SQLParam + 'a,
V::DialectMarker: ExcludedSupport,
C: Expr<'a, V> + SQLColumnInfo,
{
type SQLType = C::SQLType;
type Nullable = C::Nullable;
type Aggregate = C::Aggregate;
}
impl<'a, V, C> ToSQL<'a, V> for Excluded<C>
where
V: SQLParam + 'a,
V::DialectMarker: ExcludedSupport,
C: SQLColumnInfo,
{
fn to_sql(&self) -> SQL<'a, V> {
SQL::empty()
.push(Token::EXCLUDED)
.push(Token::DOT)
.append(SQL::ident(self.column.name()))
}
}