// This code is automatically generated by Senax and is always overwritten.

#![allow(non_snake_case)]

#[allow(clippy::module_inception)]
#[rustfmt::skip]
pub mod repositories {
@%- for (name, defs) in groups %@
    pub mod @{ name|snake|ident }@;
@%- endfor %@
}
#[rustfmt::skip]
pub mod misc {
use senax_common::ShardId;
use sqlx::query::Query;

use db::{connection::{DbArguments, DbType}, misc::TrashMode};

pub trait ColTr {
    fn name(&self) -> &'static str;
}
#[allow(clippy::len_without_is_empty)]
pub trait BindTr {
    fn name(&self) -> &'static str;
    fn placeholder(&self) -> &'static str {
        ""
    }
    fn len(&self) -> usize {
        1
    }
    fn bind_to_query(self, query: Query<'_, DbType, DbArguments>)
        -> Query<'_, DbType, DbArguments>;
}
#[allow(dead_code)]
pub trait BindArrayTr {
    fn query_each_bind(
        self,
        query: Query<'_, DbType, DbArguments>,
    ) -> Query<'_, DbType, DbArguments>;
}
pub trait ColRelTr {
    fn write_rel(&self, buf: &mut String, idx: usize, without_key: bool, shard_id: ShardId, is_outer: bool);
    fn write_key(&self, buf: &mut String);
    fn bind_to_query(self, query: Query<'_, DbType, DbArguments>)
        -> Query<'_, DbType, DbArguments>;
}
pub trait FilterTr
where
    Self: Sized,
{
    fn write(&self, buf: &mut String, idx: usize, trash_mode: &mut TrashMode, shard_id: ShardId, is_outer: bool);
    fn bind_to_query(
        self,
        query: sqlx::query::Query<'_, DbType, DbArguments>,
    ) -> sqlx::query::Query<'_, DbType, DbArguments>;
    fn write_where(
        filter: &Option<Self>,
        trash_mode: TrashMode,
        trashed_sql: &str,
        not_trashed_sql: &str,
        only_trashed_sql: &str,
        shard_id: ShardId,
    ) -> String;
}
pub trait OrderTr
where
    Self: Sized,
{
    fn write(&self, buf: &mut String);
    fn write_order(order: &Option<Vec<Self>>, raw_order: &Option<String>) -> String;
}

#[rustfmt::skip]
#[macro_export]
macro_rules! filter {
    ( $t:ty ) => {
        #[allow(bindings_with_variant_name)]
        fn write(&self, buf: &mut String, idx: usize, trash_mode: &mut TrashMode, shard_id: ShardId, is_outer: bool) {
            match self {
                Filter_::WithTrashed => {
                    *trash_mode = TrashMode::With;
                }
                Filter_::OnlyTrashed => {
                    *trash_mode = TrashMode::Only;
                }
                Filter_::IsNull(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" IS NULL AND ");
                }
                Filter_::IsNotNull(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" IS NOT NULL AND ");
                }
                Filter_::Eq(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" = ");
                    buf.push_str(c.placeholder());
                    buf.push_str(" AND ");
                }
                Filter_::EqKey(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" = ? AND ");
                }
                Filter_::NotEq(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" != ? AND ");
                }
                Filter_::Gt(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" > ");
                    buf.push_str(c.placeholder());
                    buf.push_str(" AND ");
                }
                Filter_::Gte(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" >= ");
                    buf.push_str(c.placeholder());
                    buf.push_str(" AND ");
                }
                Filter_::Lt(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" < ");
                    buf.push_str(c.placeholder());
                    buf.push_str(" AND ");
                }
                Filter_::Lte(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" <= ");
                    buf.push_str(c.placeholder());
                    buf.push_str(" AND ");
                }
                Filter_::Like(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" LIKE ? AND ");
                }
                Filter_::AllBits(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" & ? = ? AND ");
                }
                Filter_::AnyBits(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" & ? != 0 AND ");
                }
                Filter_::In(c) => {
                    if c.len() > 0 {
                        buf.push_str(c.name());
                        buf.push_str(" IN (");
                        for _i in 0..c.len() {
                            buf.push_str(c.placeholder());
                            buf.push_str(",");
                        }
                        buf.truncate(buf.len() - 1);
                        buf.push_str(") AND ");
                    } else {
                        buf.push_str("false AND ");
                    }
                }
                Filter_::NotIn(c) => {
                    if c.len() > 0 {
                        buf.push_str(c.name());
                        buf.push_str(" NOT IN (");
                        for _i in 0..c.len() {
                            buf.push_str(c.placeholder());
                            buf.push_str(",");
                        }
                        buf.truncate(buf.len() - 1);
                        buf.push_str(") AND ");
                    }
                }
@%- if config.is_mysql() %@
                Filter_::Contains(c, p) => {
                    buf.push_str("JSON_CONTAINS(");
                    if p.is_some() {
                        buf.push_str("JSON_EXTRACT(");
                        buf.push_str(c.name());
                        buf.push_str(", ?)");
                    } else {
                        buf.push_str(c.name());
                    }
                    buf.push_str(", CAST(? AS JSON)) AND ");
                    @#- JSON_EXTRACTのpathをJSON_CONTAINSの引数にするとインデックスが有効にならないので注意 #@
                    @#- Be careful because using a path as an argument for JSON_CONTAINS will prevent the index from being used. #@
                }
                Filter_::JsonIn(c, p) => {
                    buf.push_str("JSON_OVERLAPS(JSON_EXTRACT(");
                    buf.push_str(c.name());
                    buf.push_str(", ?)");
                    buf.push_str(", CAST(? AS JSON)) AND ");
                }
                Filter_::JsonContainsPath(c, _p) => {
                    buf.push_str("JSON_CONTAINS_PATH(");
                    buf.push_str(c.name());
                    buf.push_str(", 'one', ?) AND ");
                }
                Filter_::JsonEq(c, _p) => {
                    buf.push_str("JSON_EXTRACT(");
                    buf.push_str(c.name());
                    buf.push_str(", ?) = CAST(? AS JSON) AND ");
                }
                Filter_::JsonIsNull(c, _p) => {
                    buf.push_str("JSON_EXTRACT(");
                    buf.push_str(c.name());
                    buf.push_str(", ?) IS NULL AND ");
                }
                Filter_::JsonIsNotNull(c, _p) => {
                    buf.push_str("JSON_EXTRACT(");
                    buf.push_str(c.name());
                    buf.push_str(", ?) IS NOT NULL AND ");
                }
                Filter_::JsonLt(c, _p) => {
                    buf.push_str("JSON_EXTRACT(");
                    buf.push_str(c.name());
                    buf.push_str(", ?) < CAST(? AS JSON) AND ");
                }
                Filter_::JsonLte(c, _p) => {
                    buf.push_str("JSON_EXTRACT(");
                    buf.push_str(c.name());
                    buf.push_str(", ?) <= CAST(? AS JSON) AND ");
                }
                Filter_::JsonGt(c, _p) => {
                    buf.push_str("JSON_EXTRACT(");
                    buf.push_str(c.name());
                    buf.push_str(", ?) > CAST(? AS JSON) AND ");
                }
                Filter_::JsonGte(c, _p) => {
                    buf.push_str("JSON_EXTRACT(");
                    buf.push_str(c.name());
                    buf.push_str(", ?) >= CAST(? AS JSON) AND ");
                }
                Filter_::GeoEquals(c) => {
                    buf.push_str("ST_Equals(");
                    buf.push_str(c.name());
                    buf.push_str(", ST_GeomFromGeoJSON(?, 1, ?)) AND ");
                }
                Filter_::Within(c) => {
                    buf.push_str("ST_Within(");
                    buf.push_str(c.name());
                    buf.push_str(", ST_GeomFromGeoJSON(?, 1, ?)) AND ");
                }
                Filter_::Intersects(c) => {
                    buf.push_str("ST_Intersects(");
                    buf.push_str(c.name());
                    buf.push_str(", ST_GeomFromGeoJSON(?, 1, ?)) AND ");
                }
                Filter_::Crosses(c) => {
                    buf.push_str("ST_Crosses(");
                    buf.push_str(c.name());
                    buf.push_str(", ST_GeomFromGeoJSON(?, 1, ?)) AND ");
                }
                Filter_::DWithin(c) => {
                    buf.push_str("ST_Distance(");
                    buf.push_str(c.name());
                    buf.push_str(", ST_GeomFromGeoJSON(?, 1, ?)) <= ? AND ");
                    buf.push_str("ST_Intersects(");  // For searches using an index
                    buf.push_str(c.name());
                    buf.push_str(", ST_Buffer(ST_GeomFromGeoJSON(?, 1, ?), ? * 1.1)) AND ");
                }
@%- else %@
                Filter_::Contains(c, p) => {
                    if p.is_some() {
                        buf.push_str("jsonb_path_query_first(");
                        buf.push_str(c.name());
                        buf.push_str("::jsonb, ?::jsonpath)");
                    } else {
                        buf.push_str(c.name());
                        buf.push_str("::jsonb");
                    }
                    buf.push_str(" @> ?::jsonb AND ");
                }
                Filter_::JsonIn(c, p) => {
                    buf.push_str("jsonb_path_query_first(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath)");
                    buf.push_str(" <@ ?::jsonb AND ");
                }
                Filter_::JsonContainsPath(c, _p) => {
                    buf.push_str("jsonb_path_exists(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath) AND ");
                }
                Filter_::JsonEq(c, _p) => {
                    buf.push_str("jsonb_path_query_first(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath) = ?::jsonb AND ");
                }
                Filter_::JsonIsNull(c, _p) => {
                    buf.push_str("jsonb_path_query_first(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath) IS NULL AND ");
                }
                Filter_::JsonIsNotNull(c, _p) => {
                    buf.push_str("jsonb_path_query_first(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath) IS NOT NULL AND ");
                }
                Filter_::JsonLt(c, _p) => {
                    buf.push_str("jsonb_path_query_first(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath) < ?::jsonb AND ");
                }
                Filter_::JsonLte(c, _p) => {
                    buf.push_str("jsonb_path_query_first(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath) <= ?::jsonb AND ");
                }
                Filter_::JsonGt(c, _p) => {
                    buf.push_str("jsonb_path_query_first(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath) > ?::jsonb AND ");
                }
                Filter_::JsonGte(c, _p) => {
                    buf.push_str("jsonb_path_query_first(");
                    buf.push_str(c.name());
                    buf.push_str("::jsonb, ?::jsonpath) >= ?::jsonb AND ");
                }
                Filter_::GeoEquals(c) => {
                    buf.push_str("ST_Equals(");
                    buf.push_str(c.name());
                    buf.push_str("::geometry, ST_SetSRID(ST_GeomFromGeoJSON(?), ?)) AND ");
                }
                Filter_::Within(c) => {
                    buf.push_str("ST_Within(");
                    buf.push_str(c.name());
                    buf.push_str("::geometry, ST_SetSRID(ST_GeomFromGeoJSON(?), ?)) AND ");
                }
                Filter_::Intersects(c) => {
                    buf.push_str("ST_Intersects(");
                    buf.push_str(c.name());
                    buf.push_str(", ST_SetSRID(ST_GeomFromGeoJSON(?), ?)) AND ");
                }
                Filter_::Crosses(c) => {
                    buf.push_str("ST_Crosses(");
                    buf.push_str(c.name());
                    buf.push_str("::geometry, ST_SetSRID(ST_GeomFromGeoJSON(?), ?)) AND ");
                }
                Filter_::DWithin(c) => {
                    buf.push_str("ST_DWithin(");
                    buf.push_str(c.name());
                    buf.push_str(", ST_SetSRID(ST_GeomFromGeoJSON(?), ?), ?) AND ");
                }
@%- endif %@
                Filter_::Not(c) => {
                    buf.push_str("NOT (");
                    c.write(buf, idx, trash_mode, shard_id, is_outer);
                    buf.truncate(buf.len() - 5);
                    buf.push_str(") AND ");
                }
                Filter_::And(v) => {
                    if !v.is_empty() {
                        buf.push_str("(");
                        for c in v.iter() {
                            c.write(buf, idx, trash_mode, shard_id, is_outer);
                        }
                        buf.truncate(buf.len() - 5);
                        buf.push_str(") AND ");
                    } else {
                        buf.push_str("true AND ");
                    }
                }
                Filter_::Or(v) => {
                    if !v.is_empty() {
                        buf.push_str("(");
                        for c in v.iter() {
                            c.write(buf, idx, trash_mode, shard_id, is_outer);
                            buf.truncate(buf.len() - 5);
                            buf.push_str(" OR ");
                        }
                        buf.truncate(buf.len() - 4);
                        buf.push_str(") AND ");
                    } else {
                        buf.push_str("false AND ");
                    }
                }
                Filter_::Exists(c) => {
                    buf.push_str("EXISTS (");
                    c.write_rel(buf, idx, false, shard_id, is_outer);
                    buf.push_str(") AND ");
                }
                Filter_::NotExists(c) => {
                    buf.push_str("NOT EXISTS (");
                    c.write_rel(buf, idx, false, shard_id, is_outer);
                    buf.push_str(") AND ");
                }
                Filter_::EqAny(c) => {
                    c.write_key(buf);
                    buf.push_str(" = ANY (");
                    c.write_rel(buf, idx, true, shard_id, is_outer);
                    buf.push_str(") AND ");
                }
                Filter_::NotAll(c) => {
                    c.write_key(buf);
                    buf.push_str(" <> ALL (");
                    c.write_rel(buf, idx, true, shard_id, is_outer);
                    buf.push_str(") AND ");
                }
                Filter_::Raw(raw) => {
                    buf.push_str("(");
                    buf.push_str(raw@%- for db in config.outer_db() %@.replace("##@{ db|snake }@##.", ::db_@{ db|snake }@::connection::DbConn::real_db_name(shard_id)).as_str()@%- endfor %@);
                    buf.push_str(") AND ");
                }
                Filter_::RawWithParam(raw, _param) => {
                    buf.push_str("(");
                    buf.push_str(raw);
                    buf.push_str(") AND ");
                }
                Filter_::Boolean(_) => {
                    buf.push_str("? AND ");
                }
            };
        }
        fn bind_to_query(
            self,
            mut query: sqlx::query::Query<'_, DbType, DbArguments>,
        ) -> sqlx::query::Query<'_, DbType, DbArguments> {
            match self {
                Filter_::WithTrashed => query,
                Filter_::OnlyTrashed => query,
                Filter_::IsNull(_c) => query,
                Filter_::IsNotNull(_c) => query,
                Filter_::Eq(c) => c.bind_to_query(query),
                Filter_::EqKey(c) => c.bind_to_query(query),
                Filter_::NotEq(c) => c.bind_to_query(query),
                Filter_::Gt(c) => c.bind_to_query(query),
                Filter_::Gte(c) => c.bind_to_query(query),
                Filter_::Lt(c) => c.bind_to_query(query),
                Filter_::Lte(c) => c.bind_to_query(query),
                Filter_::Like(c) => c.bind_to_query(query),
                Filter_::AllBits(c) => c.bind_to_query(query),
                Filter_::AnyBits(c) => c.bind_to_query(query),
                Filter_::In(c) => c.bind_to_query(query),
                Filter_::NotIn(c) => c.bind_to_query(query),
                Filter_::Contains(c, p) => if let Some(p) = p { c.bind_to_query(query.bind(p)) } else { c.bind_to_query(query) },
                Filter_::JsonIn(c, p) => c.bind_to_query(query.bind(p)),
                Filter_::JsonContainsPath(c, p) => query.bind(p),
                Filter_::JsonEq(c, p) => c.bind_to_query(query.bind(p)),
                Filter_::JsonIsNull(c, p) => query.bind(p),
                Filter_::JsonIsNotNull(c, p) => query.bind(p),
                Filter_::JsonLt(c, p) => c.bind_to_query(query.bind(p)),
                Filter_::JsonLte(c, p) => c.bind_to_query(query.bind(p)),
                Filter_::JsonGt(c, p) => c.bind_to_query(query.bind(p)),
                Filter_::JsonGte(c, p) => c.bind_to_query(query.bind(p)),
                Filter_::GeoEquals(c) => c.bind_to_query(query),
                Filter_::Within(c) => c.bind_to_query(query),
                Filter_::Intersects(c) => c.bind_to_query(query),
                Filter_::Crosses(c) => c.bind_to_query(query),
                Filter_::DWithin(c) => c.bind_to_query(query),
                Filter_::Not(c) => c.bind_to_query(query),
                Filter_::And(v) => {for c in v { query = c.bind_to_query(query); } query},
                Filter_::Or(v) => {for c in v { query = c.bind_to_query(query); } query},
                Filter_::Exists(c) => c.bind_to_query(query),
                Filter_::NotExists(c) => c.bind_to_query(query),
                Filter_::EqAny(c) => c.bind_to_query(query),
                Filter_::NotAll(c) => c.bind_to_query(query),
                Filter_::Raw(_c) => query,
                Filter_::RawWithParam(_c, param) => {for v in param { query = query.bind(v); } query},
                Filter_::Boolean(v) => query.bind(v),
            }
        }
        #[inline(never)]
        fn write_where(
            filter: &Option<Filter_>,
            mut trash_mode: TrashMode,
            trashed_sql: &str,
            not_trashed_sql: &str,
            only_trashed_sql: &str,
            shard_id: ShardId,
        ) -> String {
            let mut s = String::with_capacity(1000);
            s.push_str("WHERE ");
            if let Some(c) = filter {
                c.write(&mut s, 1, &mut trash_mode, shard_id, false);
            }
            if trash_mode == TrashMode::Not {
                s.push_str(not_trashed_sql)
            } else if trash_mode == TrashMode::Only {
                s.push_str(only_trashed_sql)
            } else {
                s.push_str(trashed_sql)
            }
            if s.len() > "WHERE ".len() {
                s.truncate(s.len() - " AND ".len());
            } else {
                s.truncate(0);
            }
            s
        }
    };
}
pub use filter;

#[macro_export]
macro_rules! order {
    () => {
        #[inline(never)]
        fn write(&self, buf: &mut String) {
            match self {
                Order_::Asc(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" ASC, ");
                }
                Order_::Desc(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" DESC, ");
                }
                Order_::IsNullAsc(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" IS NULL ASC, ");
                }
                Order_::IsNullDesc(c) => {
                    buf.push_str(c.name());
                    buf.push_str(" IS NULL DESC, ");
                }
            };
        }
        #[inline(never)]
        fn write_order(order: &Option<Vec<Order_>>, raw_order: &Option<String>) -> String {
            if let Some(raw_order) = raw_order {
                return format!("ORDER BY {}", raw_order);
            }
            match order {
                Some(v) if !v.is_empty() => {
                    let mut s = String::with_capacity(100);
                    s.push_str("ORDER BY ");
                    for o in v {
                        o.write(&mut s);
                    }
                    s.truncate(s.len() - 2);
                    s
                }
                _ => String::new(),
            }
        }
    };
}
pub use order;
}
@{-"\n"}@