sim-relation-plan 0.2.0

Admission and sealed checked logical relational plans for SIM.
Documentation
use crate::model::*;
use sim_kernel::{Datum, Symbol};
use sim_relation_core::{ColumnName, RowType, ToRelationDatum};

pub(crate) struct QueryDatum<'a> {
    pub(crate) raw: &'a Rel,
    pub(crate) parameters: &'a RowType,
}
pub(crate) struct MutationDatum<'a>(pub(crate) &'a Mutation);
impl ToRelationDatum for QueryDatum<'_> {
    fn to_datum(&self) -> Datum {
        node(
            "query",
            vec![
                ("parameters", self.parameters.to_datum()),
                ("plan", rel_datum(self.raw)),
            ],
        )
    }
}
impl ToRelationDatum for MutationDatum<'_> {
    fn to_datum(&self) -> Datum {
        mutation_datum(self.0)
    }
}
fn rel_datum(v: &Rel) -> Datum {
    match v {
        Rel::Scan {
            source,
            table,
            bind,
        } => node(
            "scan",
            vec![
                ("source", symbol(source.symbol())),
                ("table", symbol(table.symbol())),
                ("bind", symbol(bind.symbol())),
            ],
        ),
        Rel::Values {
            bind,
            row_type,
            rows,
        } => node(
            "values",
            vec![
                ("bind", symbol(bind.symbol())),
                ("row-type", row_type.to_datum()),
                ("rows", list(rows.iter().map(ToRelationDatum::to_datum))),
            ],
        ),
        Rel::Project {
            input,
            bind,
            fields,
        } => node(
            "project",
            vec![
                ("input", rel_datum(input)),
                ("bind", symbol(bind.symbol())),
                ("fields", list(fields.iter().map(named_datum))),
            ],
        ),
        Rel::Filter { input, predicate } => node(
            "filter",
            vec![
                ("input", rel_datum(input)),
                ("predicate", scalar_datum(predicate)),
            ],
        ),
        Rel::Join {
            left,
            right,
            kind,
            on,
        } => node(
            "join",
            vec![
                ("left", rel_datum(left)),
                ("right", rel_datum(right)),
                (
                    "kind",
                    word(match kind {
                        JoinKind::Inner => "inner",
                        JoinKind::Left => "left",
                        JoinKind::Cross => "cross",
                    }),
                ),
                ("on", scalar_datum(on)),
            ],
        ),
        Rel::Group {
            input,
            bind,
            keys,
            aggregates,
            having,
        } => node(
            "group",
            vec![
                ("input", rel_datum(input)),
                ("bind", symbol(bind.symbol())),
                ("keys", list(keys.iter().map(named_datum))),
                (
                    "aggregates",
                    list(aggregates.iter().map(|v| {
                        node(
                            "named-aggregate",
                            vec![
                                ("name", symbol(v.name.symbol())),
                                ("aggregate", aggregate_datum(&v.aggregate)),
                            ],
                        )
                    })),
                ),
                ("having", option(having.as_ref().map(scalar_datum))),
            ],
        ),
        Rel::Set { op, inputs } => node(
            "set",
            vec![
                (
                    "op",
                    word(match op {
                        SetOp::Union => "union",
                        SetOp::UnionAll => "union-all",
                        SetOp::Intersect => "intersect",
                        SetOp::Except => "except",
                    }),
                ),
                ("inputs", list(inputs.iter().map(rel_datum))),
            ],
        ),
        Rel::Distinct(input) => node("distinct", vec![("input", rel_datum(input))]),
        Rel::Order { input, keys } => node(
            "order",
            vec![
                ("input", rel_datum(input)),
                (
                    "keys",
                    list(keys.iter().map(|v| {
                        node(
                            "order-key",
                            vec![
                                ("scalar", scalar_datum(&v.scalar)),
                                (
                                    "direction",
                                    word(match v.direction {
                                        OrderDirection::Asc => "asc",
                                        OrderDirection::Desc => "desc",
                                    }),
                                ),
                            ],
                        )
                    })),
                ),
            ],
        ),
        Rel::Limit {
            input,
            count,
            offset,
        } => node(
            "limit",
            vec![
                ("input", rel_datum(input)),
                ("count", option(count.map(number))),
                ("offset", number(*offset)),
            ],
        ),
    }
}
fn scalar_datum(v: &Scalar) -> Datum {
    match v {
        Scalar::Field(v) => node(
            "field",
            vec![
                ("binding", symbol(v.binding.symbol())),
                ("field", symbol(v.field.symbol())),
            ],
        ),
        Scalar::Literal(v) => node("literal", vec![("cell", v.to_datum())]),
        Scalar::Param(v) => node("parameter", vec![("name", symbol(v.symbol()))]),
        Scalar::Call(op, args) => node(
            "call",
            vec![
                (
                    "operator",
                    word(match op {
                        ScalarOp::And => "and",
                        ScalarOp::Or => "or",
                        ScalarOp::Not => "not",
                        ScalarOp::Eq => "eq",
                        ScalarOp::Ne => "ne",
                        ScalarOp::Lt => "lt",
                        ScalarOp::Le => "le",
                        ScalarOp::Gt => "gt",
                        ScalarOp::Ge => "ge",
                        ScalarOp::Add => "add",
                        ScalarOp::Sub => "sub",
                        ScalarOp::Mul => "mul",
                        ScalarOp::Div => "div",
                        ScalarOp::IsNull => "is-null",
                        ScalarOp::Coalesce => "coalesce",
                    }),
                ),
                ("arguments", list(args.iter().map(scalar_datum))),
            ],
        ),
        Scalar::Case {
            branches,
            otherwise,
        } => node(
            "case",
            vec![
                (
                    "branches",
                    list(branches.iter().map(|(p, v)| {
                        node(
                            "branch",
                            vec![("when", scalar_datum(p)), ("then", scalar_datum(v))],
                        )
                    })),
                ),
                ("otherwise", option(otherwise.as_deref().map(scalar_datum))),
            ],
        ),
        Scalar::Exists(v) => node("exists", vec![("query", rel_datum(v))]),
        Scalar::InQuery { value, query } => node(
            "in-query",
            vec![("value", scalar_datum(value)), ("query", rel_datum(query))],
        ),
        Scalar::ScalarQuery(v) => node("scalar-query", vec![("query", rel_datum(v))]),
    }
}
fn aggregate_datum(v: &Aggregate) -> Datum {
    match v {
        Aggregate::CountAll => node("count-all", vec![]),
        Aggregate::Count(v) => node("count", vec![("value", scalar_datum(v))]),
        Aggregate::Sum(v) => node("sum", vec![("value", scalar_datum(v))]),
        Aggregate::Min(v) => node("min", vec![("value", scalar_datum(v))]),
        Aggregate::Max(v) => node("max", vec![("value", scalar_datum(v))]),
    }
}
fn named_datum(v: &NamedScalar) -> Datum {
    node(
        "named-scalar",
        vec![
            ("name", symbol(v.name.symbol())),
            ("scalar", scalar_datum(&v.scalar)),
        ],
    )
}
fn target_datum(v: &ConflictTarget) -> Datum {
    match v {
        ConflictTarget::PrimaryKey => node("primary-key", vec![]),
        ConflictTarget::UniqueConstraint(n) => {
            node("unique-constraint", vec![("name", symbol(n.symbol()))])
        }
        ConflictTarget::Columns(v) => node(
            "unique-columns",
            vec![("columns", list(v.iter().map(|n| symbol(n.symbol()))))],
        ),
    }
}
fn conflict_datum(v: &ConflictAction) -> Datum {
    match v {
        ConflictAction::Fail => node("fail", vec![]),
        ConflictAction::DoNothing { target } => {
            node("do-nothing", vec![("target", target_datum(target))])
        }
        ConflictAction::DoUpdate {
            target,
            assignments,
            predicate,
        } => node(
            "do-update",
            vec![
                ("target", target_datum(target)),
                ("assignments", assignments_datum(assignments)),
                ("predicate", option(predicate.as_ref().map(scalar_datum))),
            ],
        ),
    }
}
fn assignments_datum(v: &[(ColumnName, Scalar)]) -> Datum {
    list(v.iter().map(|(n, v)| {
        node(
            "assignment",
            vec![("column", symbol(n.symbol())), ("value", scalar_datum(v))],
        )
    }))
}
fn mutation_datum(v: &Mutation) -> Datum {
    match v {
        Mutation::Insert {
            table,
            columns,
            input,
            conflict,
            returning,
        } => node(
            "insert",
            vec![
                ("table", symbol(table.symbol())),
                ("columns", list(columns.iter().map(|n| symbol(n.symbol())))),
                ("input", rel_datum(input)),
                ("conflict", conflict_datum(conflict)),
                ("returning", list(returning.iter().map(named_datum))),
            ],
        ),
        Mutation::Update {
            table,
            bind,
            assignments,
            predicate,
            returning,
        } => node(
            "update",
            vec![
                ("table", symbol(table.symbol())),
                ("bind", symbol(bind.symbol())),
                ("assignments", assignments_datum(assignments)),
                ("predicate", option(predicate.as_ref().map(scalar_datum))),
                ("returning", list(returning.iter().map(named_datum))),
            ],
        ),
        Mutation::Delete {
            table,
            bind,
            predicate,
            returning,
        } => node(
            "delete",
            vec![
                ("table", symbol(table.symbol())),
                ("bind", symbol(bind.symbol())),
                ("predicate", option(predicate.as_ref().map(scalar_datum))),
                ("returning", list(returning.iter().map(named_datum))),
            ],
        ),
    }
}
fn symbol(v: &Symbol) -> Datum {
    Datum::Symbol(v.clone())
}
fn word(v: &str) -> Datum {
    Datum::Symbol(Symbol::new(v))
}
fn list(v: impl IntoIterator<Item = Datum>) -> Datum {
    Datum::Vector(v.into_iter().collect())
}
fn option(v: Option<Datum>) -> Datum {
    v.unwrap_or(Datum::Nil)
}
fn number(v: u64) -> Datum {
    Datum::Number(sim_kernel::NumberLiteral {
        domain: Symbol::qualified("core", "u64"),
        canonical: v.to_string(),
    })
}
fn node(tag: &str, fields: Vec<(&str, Datum)>) -> Datum {
    Datum::Node {
        tag: Symbol::qualified("relation-plan", tag),
        fields: fields
            .into_iter()
            .map(|(k, v)| (Symbol::new(k), v))
            .collect(),
    }
}