#[cfg(not(feature = "std"))]
use crate::prelude::*;
use crate::traits::PostgresTable;
use crate::values::PostgresValue;
use drizzle_core::{SQL, SQLTableInfo, ToSQL, Token, helpers, traits::SQLModel};
pub(crate) use helpers::{
delete, except, except_all, from, group_by_expr, having, intersect, intersect_all, limit,
offset, order_by, select, select_distinct, set, union, union_all, update, r#where,
};
pub use drizzle_core::Join;
#[doc(hidden)]
pub trait JoinSource<'a>: join_source_private::Sealed {
type JoinedTable;
fn into_join_source_sql(self) -> SQL<'a, PostgresValue<'a>>;
}
mod join_source_private {
pub trait Sealed {}
}
impl<'a, Table> join_source_private::Sealed for Table where Table: PostgresTable<'a> {}
impl<'a, Name, Projection, Query> join_source_private::Sealed
for drizzle_core::Derived<'a, PostgresValue<'a>, Name, Projection, Query>
where
Name: drizzle_core::Tag,
Projection: drizzle_core::DerivedProjection<Name>,
Query: ToSQL<'a, PostgresValue<'a>>,
{
}
impl<'a, Table> JoinSource<'a> for Table
where
Table: PostgresTable<'a>,
{
type JoinedTable = Table;
fn into_join_source_sql(self) -> SQL<'a, PostgresValue<'a>> {
self.into_sql()
}
}
impl<'a, Name, Projection, Query> JoinSource<'a>
for drizzle_core::Derived<'a, PostgresValue<'a>, Name, Projection, Query>
where
Name: drizzle_core::Tag,
Projection: drizzle_core::DerivedProjection<Name>,
Query: ToSQL<'a, PostgresValue<'a>>,
{
type JoinedTable = Self;
fn into_join_source_sql(self) -> SQL<'a, PostgresValue<'a>> {
self.into_sql()
}
}
#[doc(hidden)]
pub trait CrossJoinArg<'a, FromTable>: cross_join_arg_private::Sealed {
type JoinedTable;
fn into_cross_join_sql(self) -> SQL<'a, PostgresValue<'a>>;
}
mod cross_join_arg_private {
pub trait Sealed {}
impl<'a, Source> Sealed for Source where Source: super::JoinSource<'a> {}
impl<'a, Source, Condition> Sealed for (Source, Condition)
where
Source: super::JoinSource<'a>,
Condition: drizzle_core::ToSQL<'a, crate::values::PostgresValue<'a>>,
{
}
}
impl<'a, Source, FromTable> CrossJoinArg<'a, FromTable> for Source
where
Source: JoinSource<'a>,
{
type JoinedTable = Source::JoinedTable;
fn into_cross_join_sql(self) -> SQL<'a, PostgresValue<'a>> {
Join::new()
.cross()
.into_sql()
.append(self.into_join_source_sql())
}
}
impl<'a, Source, Condition, FromTable> CrossJoinArg<'a, FromTable> for (Source, Condition)
where
Source: JoinSource<'a>,
Condition: ToSQL<'a, PostgresValue<'a>>,
{
type JoinedTable = Source::JoinedTable;
fn into_cross_join_sql(self) -> SQL<'a, PostgresValue<'a>> {
let (source, condition) = self;
Join::new()
.inner()
.into_sql()
.append(source.into_join_source_sql())
.push(Token::ON)
.append(condition.into_sql())
}
}
drizzle_core::impl_join_arg_trait!(
table_trait: PostgresTable<'a>,
table_info_trait: SQLTableInfo,
condition_trait: ToSQL<'a, PostgresValue<'a>>,
join_source_trait: JoinSource<'a>,
value_type: PostgresValue<'a>,
);
drizzle_core::impl_join_helpers!(
table_trait: PostgresTable<'a>,
condition_trait: ToSQL<'a, PostgresValue<'a>>,
sql_type: SQL<'a, PostgresValue<'a>>,
);
pub(crate) fn select_distinct_on<'a, On, Columns>(
on: On,
columns: Columns,
) -> SQL<'a, PostgresValue<'a>>
where
On: ToSQL<'a, PostgresValue<'a>>,
Columns: ToSQL<'a, PostgresValue<'a>>,
{
SQL::from_iter([Token::SELECT, Token::DISTINCT, Token::ON, Token::LPAREN])
.append(on.into_sql())
.push(Token::RPAREN)
.append(columns.into_sql())
}
fn join_using_internal<'a, Table>(
table: Table,
join: Join,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join.into_sql()
.append(table.into_sql())
.push(Token::USING)
.push(Token::LPAREN)
.append(columns.into_sql())
.push(Token::RPAREN)
}
pub fn join_using<'a, Table>(
table: Table,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join_using_internal(table, Join::new(), columns)
}
pub fn inner_join_using<'a, Table>(
table: Table,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join_using_internal(table, Join::new().inner(), columns)
}
pub fn left_join_using<'a, Table>(
table: Table,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join_using_internal(table, Join::new().left(), columns)
}
pub fn left_outer_join_using<'a, Table>(
table: Table,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join_using_internal(table, Join::new().left().outer(), columns)
}
pub fn right_join_using<'a, Table>(
table: Table,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join_using_internal(table, Join::new().right(), columns)
}
pub fn right_outer_join_using<'a, Table>(
table: Table,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join_using_internal(table, Join::new().right().outer(), columns)
}
pub fn full_join_using<'a, Table>(
table: Table,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join_using_internal(table, Join::new().full(), columns)
}
pub fn full_outer_join_using<'a, Table>(
table: Table,
columns: impl ToSQL<'a, PostgresValue<'a>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
join_using_internal(table, Join::new().full().outer(), columns)
}
pub(crate) fn insert<'a, Table>(table: &Table) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
SQL::from_iter([Token::INSERT, Token::INTO]).append(table)
}
pub(crate) fn values<'a, Table, T>(
rows: impl IntoIterator<Item = Table::Insert<T>>,
) -> SQL<'a, PostgresValue<'a>>
where
Table: PostgresTable<'a>,
{
let rows: Vec<_> = rows.into_iter().collect();
if rows.is_empty() {
return SQL::from(Token::VALUES);
}
let columns_info = rows[0].columns();
let columns_slice = columns_info.as_ref();
if rows[1..]
.iter()
.any(|row| row.columns().as_ref() != columns_slice)
{
let rows_sql = drizzle_core::helpers::insert_values_with_defaults(
rows.iter()
.map(|row| (row.columns(), row.values()))
.collect(),
);
if let Some(rows_sql) = rows_sql {
return rows_sql;
}
}
if columns_slice.is_empty() {
return if rows.len() == 1 {
SQL::from_iter([Token::DEFAULT, Token::VALUES])
} else {
SQL::raw("SELECT FROM").append(SQL::func(
"generate_series",
SQL::number(1)
.push(Token::COMMA)
.append(SQL::number(rows.len())),
))
};
}
let columns_sql = SQL::columns(columns_slice);
let mut values_sql = SQL::with_capacity_chunks(rows.len().saturating_mul(4));
for (idx, row) in rows.iter().enumerate() {
if idx > 0 {
values_sql.push_mut(Token::COMMA);
}
values_sql.push_mut(Token::LPAREN);
values_sql.append_mut(row.values());
values_sql.push_mut(Token::RPAREN);
}
columns_sql.parens().push(Token::VALUES).append(values_sql)
}
pub(crate) fn returning<'a, 'b, I>(columns: I) -> SQL<'a, PostgresValue<'a>>
where
I: ToSQL<'a, PostgresValue<'a>>,
{
let columns = columns.into_sql();
let columns = if columns.chunks.is_empty() {
SQL::from(Token::STAR)
} else {
columns
};
SQL::from(Token::RETURNING).append(columns)
}
pub(crate) fn for_update<'a>() -> SQL<'a, PostgresValue<'a>> {
SQL::from_iter([Token::FOR, Token::UPDATE])
}
pub(crate) fn for_share<'a>() -> SQL<'a, PostgresValue<'a>> {
SQL::from_iter([Token::FOR, Token::SHARE])
}
pub(crate) fn for_no_key_update<'a>() -> SQL<'a, PostgresValue<'a>> {
SQL::from_iter([Token::FOR, Token::NO, Token::KEY, Token::UPDATE])
}
pub(crate) fn for_key_share<'a>() -> SQL<'a, PostgresValue<'a>> {
SQL::from_iter([Token::FOR, Token::KEY, Token::SHARE])
}
pub(crate) fn for_update_of<'a>(table_name: &str) -> SQL<'a, PostgresValue<'a>> {
SQL::from_iter([Token::FOR, Token::UPDATE, Token::OF])
.append(SQL::ident(String::from(table_name)))
}
pub(crate) fn for_share_of<'a>(table_name: &str) -> SQL<'a, PostgresValue<'a>> {
SQL::from_iter([Token::FOR, Token::SHARE, Token::OF])
.append(SQL::ident(String::from(table_name)))
}
pub(crate) fn nowait<'a>() -> SQL<'a, PostgresValue<'a>> {
SQL::from(Token::NOWAIT)
}
pub(crate) fn skip_locked<'a>() -> SQL<'a, PostgresValue<'a>> {
SQL::from_iter([Token::SKIP, Token::LOCKED])
}