use ciborium::Value as CborValue;
use vantage_core::Result;
use vantage_expressions::traits::datasource::ExprDataSource;
use vantage_expressions::{Expression, expr_any};
use vantage_table::table::Table;
use vantage_table::traits::column_like::ColumnLike;
use vantage_table::traits::table_source::TableSource;
use vantage_types::{Entity, Record};
pub(crate) trait SetWrites:
TableSource<Id = String> + ExprDataSource<<Self as TableSource>::Value>
{
fn id_param<E: Entity<Self::Value>>(table: &Table<Self, E>, id: &str) -> Self::Value;
fn id_cell<E: Entity<Self::Value>>(table: &Table<Self, E>, id: &str) -> Self::Value;
fn null() -> Self::Value;
fn absent(_column_type: Option<&str>) -> Expression<Self::Value> {
let null = Self::null();
expr_any!("{}", null)
}
fn quoted(name: &str) -> Expression<Self::Value>;
fn condition_expr(condition: &Self::Condition) -> Expression<Self::Value>;
fn into_cbor(value: Self::Value) -> CborValue;
}
pub(crate) async fn id_exists_anywhere<T, E>(db: &T, table: &Table<T, E>, id: &str) -> Result<bool>
where
T: SetWrites,
E: Entity<T::Value>,
{
let id_field = T::quoted(&table.id_field_name());
let id_val = T::id_param(table, id);
let probe: Expression<T::Value> = expr_any!(
"SELECT 1 AS {} FROM {} WHERE {} = {} LIMIT 1",
(id_field),
(T::quoted(table.table_name())),
(id_field),
id_val
);
Ok(has_rows::<T>(db.execute(&probe).await?))
}
pub(crate) async fn row_in_set<T, E>(
db: &T,
table: &Table<T, E>,
row: &Record<T::Value>,
) -> Result<bool>
where
T: SetWrites,
E: Entity<T::Value>,
{
if table.conditions().next().is_none() {
return Ok(true);
}
let mut names: Vec<String> = table
.columns()
.keys()
.filter(|n| !table.is_calculated_column(n))
.cloned()
.collect();
for key in row.keys() {
if !names.contains(key) {
names.push(key.clone());
}
}
let fields: Vec<Expression<T::Value>> = names
.iter()
.map(|name| {
let cell: Expression<T::Value> = match row.get(name) {
Some(value) => {
let value = value.clone();
expr_any!("{}", value)
}
None => T::absent(table.columns().get(name).map(|c| c.get_type())),
};
expr_any!("{} AS {}", (cell), (T::quoted(name)))
})
.collect();
let conditions: Vec<Expression<T::Value>> = table
.conditions()
.map(|c| expr_any!("({})", (T::condition_expr(c))))
.collect();
let probe: Expression<T::Value> = expr_any!(
"SELECT 1 AS {} FROM (SELECT {}) AS {} WHERE {}",
(T::quoted(&table.id_field_name())),
(Expression::from_vec(fields, ", ")),
(T::quoted(table.table_name())),
(Expression::from_vec(conditions, " AND "))
);
Ok(has_rows::<T>(db.execute(&probe).await?))
}
fn has_rows<T: SetWrites>(result: T::Value) -> bool {
matches!(T::into_cbor(result), CborValue::Array(rows) if !rows.is_empty())
}