use super::types::*;
use crate::api_request::{
ApiRequest, JoinType, QualifiedIdentifier, Range, SelectItem,
};
use crate::error::{Error, Result};
use crate::schema_cache::{Relationship, SchemaCache, Table};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ReadPlan {
pub select: Vec<CoercibleSelectField>,
pub from: QualifiedIdentifier,
pub from_alias: Option<String>,
pub where_clauses: Vec<CoercibleLogicTree>,
pub order: Vec<CoercibleOrderTerm>,
pub range: Range,
pub rel_name: String,
pub rel_to_parent: Option<Relationship>,
pub rel_join_conds: Vec<JoinCondition>,
pub rel_join_type: Option<JoinType>,
pub rel_select: Vec<RelSelectField>,
pub depth: u32,
}
impl ReadPlan {
pub fn from_request(
request: &ApiRequest,
table: &Table,
schema_cache: &SchemaCache,
) -> Result<Self> {
let qi = table.qualified_identifier();
let select = build_select_fields(&request.query_params.select, table)?;
let where_clauses = build_where_clauses(request, table)?;
let order = build_order_terms(request, table)?;
let rel_select = build_relation_selects(&request.query_params.select, table, schema_cache)?;
Ok(Self {
select,
from: qi,
from_alias: None,
where_clauses,
order,
range: request.top_level_range.clone(),
rel_name: table.name.clone(),
rel_to_parent: None,
rel_join_conds: vec![],
rel_join_type: None,
rel_select,
depth: 0,
})
}
pub fn for_mutation(
request: &ApiRequest,
table: &Table,
schema_cache: &SchemaCache,
) -> Result<Self> {
let mut plan = Self::from_request(request, table, schema_cache)?;
plan.from_alias = Some("pgrst_mutation_result".to_string());
Ok(plan)
}
pub fn has_where(&self) -> bool {
!self.where_clauses.is_empty()
}
pub fn has_order(&self) -> bool {
!self.order.is_empty()
}
pub fn has_pagination(&self) -> bool {
self.range.limit.is_some() || self.range.offset > 0
}
}
fn build_select_fields(
items: &[SelectItem],
table: &Table,
) -> Result<Vec<CoercibleSelectField>> {
if items.is_empty() {
return Ok(table
.columns
.iter()
.map(|(name, col)| CoercibleSelectField::simple(name, &col.data_type))
.collect());
}
let mut fields = Vec::new();
for item in items {
match item {
SelectItem::Field {
field,
aggregate,
aggregate_cast,
cast,
alias,
} => {
let column = table
.get_column(&field.name)
.ok_or_else(|| Error::ColumnNotFound(field.name.clone()))?;
fields.push(CoercibleSelectField {
field: CoercibleField::from_field(field, &column.data_type),
aggregate: aggregate.clone(),
aggregate_cast: aggregate_cast.clone(),
cast: cast.clone(),
alias: alias.clone(),
});
}
SelectItem::Relation { .. } | SelectItem::SpreadRelation { .. } => {}
}
}
Ok(fields)
}
fn build_where_clauses(
request: &ApiRequest,
table: &Table,
) -> Result<Vec<CoercibleLogicTree>> {
let type_resolver = |name: &str| -> String {
table
.get_column(name)
.map(|c| c.data_type.clone())
.unwrap_or_else(|| "text".to_string())
};
let mut clauses = Vec::new();
for filter in &request.query_params.filters_root {
let pg_type = type_resolver(&filter.field.name);
clauses.push(CoercibleLogicTree::Stmt(CoercibleFilter::from_filter(
filter, &pg_type,
)));
}
for (path, tree) in &request.query_params.logic {
if path.is_empty() {
clauses.push(CoercibleLogicTree::from_logic_tree(tree, type_resolver));
}
}
Ok(clauses)
}
fn build_order_terms(
request: &ApiRequest,
table: &Table,
) -> Result<Vec<CoercibleOrderTerm>> {
let mut terms = Vec::new();
for (path, order_terms) in &request.query_params.order {
if path.is_empty() {
for term in order_terms {
let field_name = match term {
crate::api_request::OrderTerm::Field { field, .. } => &field.name,
crate::api_request::OrderTerm::Relation { field, .. } => &field.name,
};
let pg_type = table
.get_column(field_name)
.map(|c| c.data_type.as_str())
.unwrap_or("text");
terms.push(CoercibleOrderTerm::from_order_term(term, pg_type));
}
}
}
Ok(terms)
}
fn build_relation_selects(
items: &[SelectItem],
table: &Table,
schema_cache: &SchemaCache,
) -> Result<Vec<RelSelectField>> {
let mut rel_selects = Vec::new();
for item in items {
match item {
SelectItem::Relation {
relation,
alias,
hint: _,
join_type,
} => {
let _rel = schema_cache
.find_relationship(&table.qualified_identifier(), relation, &table.schema)
.ok_or_else(|| Error::RelationshipNotFound(relation.clone()))?;
rel_selects.push(RelSelectField {
name: relation.clone(),
agg_alias: alias.clone().unwrap_or_else(|| format!("pgrst_{}", relation)),
join_type: join_type.clone().unwrap_or_default(),
is_spread: false,
});
}
SelectItem::SpreadRelation {
relation,
hint: _,
join_type,
} => {
let _rel = schema_cache
.find_relationship(&table.qualified_identifier(), relation, &table.schema)
.ok_or_else(|| Error::RelationshipNotFound(relation.clone()))?;
rel_selects.push(RelSelectField {
name: relation.clone(),
agg_alias: format!("pgrst_spread_{}", relation),
join_type: join_type.clone().unwrap_or_default(),
is_spread: true,
});
}
_ => {}
}
}
Ok(rel_selects)
}
#[derive(Clone, Debug)]
pub struct ReadPlanTree {
pub root: ReadPlan,
pub children: Vec<ReadPlanTree>,
}
impl ReadPlanTree {
pub fn empty() -> Self {
Self {
root: ReadPlan {
select: vec![],
from: QualifiedIdentifier::unqualified(""),
from_alias: None,
where_clauses: vec![],
order: vec![],
range: Range::default(),
rel_name: String::new(),
rel_to_parent: None,
rel_join_conds: vec![],
rel_join_type: None,
rel_select: vec![],
depth: 0,
},
children: vec![],
}
}
pub fn leaf(plan: ReadPlan) -> Self {
Self {
root: plan,
children: vec![],
}
}
pub fn add_child(&mut self, child: ReadPlanTree) {
self.children.push(child);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_read_plan_tree_empty() {
let tree = ReadPlanTree::empty();
assert!(tree.root.select.is_empty());
assert!(tree.children.is_empty());
}
}