sea-schema-sync 0.17.0-rc.16

🌿 SQL schema definition and discovery
Documentation
use super::SchemaQueryBuilder;
use crate::rusqlite_types::RusqliteRow;
use sea_query::{
    Condition, DynIden, Expr, ExprTrait, Iden, JoinType, Order, Query, SelectStatement,
};

#[derive(Debug, Iden)]
pub enum PgIndexes {
    Table,
    #[iden = "tablename"]
    TableName,
    #[iden = "schemaname"]
    SchemaName,
    #[iden = "indexname"]
    IndexName,
}

#[derive(Debug, Iden)]
pub enum PgIndex {
    Table,
    #[iden = "indexrelid"]
    IndexRelId,
    #[iden = "indrelid"]
    IndRelId,
    #[iden = "indisunique"]
    IndIsUnique,
    #[iden = "indisprimary"]
    IndIsPrimary,
    #[iden = "indpred"]
    IndPred,
    #[iden = "indkey"]
    IndKey,
}

#[derive(Debug, Iden)]
pub enum PgClass {
    Table,
    Oid,
    #[iden = "relnamespace"]
    RelNamespace,
    #[iden = "relname"]
    RelName,
}

#[derive(Debug, Iden)]
pub enum PgNamespace {
    Table,
    Oid,
    #[iden = "nspname"]
    NspName,
}

#[derive(Debug, Iden)]
pub enum PgAttribute {
    Table,
    Oid,
    #[iden = "attrelid"]
    AttRelId,
    #[iden = "attname"]
    AttName,
    #[iden = "attnum"]
    AttNum,
}

#[derive(Debug, Default)]
pub struct UniqueIndexQueryResult {
    pub index_name: String,
    pub table_schema: String,
    pub table_name: String,
    pub column_name: String,
    pub is_partial: bool,
}

impl SchemaQueryBuilder {
    pub fn query_table_unique_indexes(&self, schema: DynIden, table: DynIden) -> SelectStatement {
        let idx = "idx";
        let insp = "insp";
        let tbl = "tbl";
        let tnsp = "tnsp";
        let col = "col";
        let partially = "partially";

        Query::select()
            .column((idx, PgClass::RelName))
            .column((insp, PgNamespace::NspName))
            .column((tbl, PgClass::RelName))
            .column((col, PgAttribute::AttName))
            .expr_as(Expr::col(PgIndex::IndPred).is_not_null(), partially)
            .from(PgIndex::Table)
            .join_as(
                JoinType::Join,
                PgClass::Table,
                idx,
                Expr::col((idx, PgClass::Oid)).equals((PgIndex::Table, PgIndex::IndexRelId)),
            )
            .join_as(
                JoinType::Join,
                PgNamespace::Table,
                insp,
                Expr::col((insp, PgNamespace::Oid)).equals((idx, PgClass::RelNamespace)),
            )
            .join_as(
                JoinType::Join,
                PgClass::Table,
                tbl,
                Expr::col((tbl, PgClass::Oid)).equals((PgIndex::Table, PgIndex::IndRelId)),
            )
            .join_as(
                JoinType::Join,
                PgNamespace::Table,
                tnsp,
                Expr::col((tnsp, PgNamespace::Oid)).equals((tbl, PgClass::RelNamespace)),
            )
            .join_as(
                JoinType::Join,
                PgAttribute::Table,
                col,
                Expr::col((col, PgAttribute::AttRelId)).equals((tbl, PgAttribute::Oid)),
            )
            .join_lateral(
                JoinType::Join,
                Query::select()
                    .expr_as(
                        Expr::cust("generate_subscripts(pg_index.indkey, 1)"),
                        "subscript",
                    )
                    .take(),
                "sub",
                Expr::col((col, PgAttribute::AttNum))
                    .eq(Expr::cust("pg_index.indkey[sub.subscript]")),
            )
            .cond_where(
                Condition::all()
                    .add(Expr::col((PgIndex::Table, PgIndex::IndIsUnique)).eq(true))
                    .add(Expr::col((PgIndex::Table, PgIndex::IndIsPrimary)).eq(false))
                    .add(Expr::col((tbl, PgClass::RelName)).eq(table.to_string()))
                    .add(Expr::col((tnsp, PgNamespace::NspName)).eq(schema.to_string())),
            )
            .order_by((PgIndex::Table, PgIndex::IndexRelId), Order::Asc)
            .order_by(("sub", "subscript"), Order::Asc)
            .take()
    }
}

#[cfg(feature = "sqlx-postgres")]
impl From<RusqliteRow> for UniqueIndexQueryResult {
    fn from(row: RusqliteRow) -> Self {
        use crate::rusqlite_types::Row;
        let row = row.postgres();
        Self {
            index_name: row.get(0),
            table_schema: row.get(1),
            table_name: row.get(2),
            column_name: row.get(3),
            is_partial: row.get(4),
        }
    }
}

#[cfg(not(feature = "sqlx-postgres"))]
impl From<RusqliteRow> for UniqueIndexQueryResult {
    fn from(_: RusqliteRow) -> Self {
        Self::default()
    }
}