use std::rc::Rc;
use std::sync::Arc;
use crate::ast::from_clause::HamelinFromClause;
use crate::ast::pipeline::HamelinPipeline;
use crate::env::Environment;
use crate::translation::projection_builder::{ProjectionBuilder, ProjectionBuilderExt};
use crate::translation::sql_query_helpers::{add_filter_condition, prepend_projections};
use crate::translation::PendingQuery;
use hamelin_lib::antlr::hamelinparser::FromClauseContextAll;
use hamelin_lib::err::{TranslationError, TranslationErrors};
use hamelin_lib::sql::query::set::SetOperation;
use hamelin_lib::sql::query::{SQLQuery, SQLQueryExpression, SubQuery};
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FromAliasing<'a> {
AliasingAllowed,
RaiseErrorOnAlias(&'a str),
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FromAnonymous {
DefaultAlias,
DoNotNest,
}
pub fn uber_from(
ctx: Vec<Rc<FromClauseContextAll<'static>>>,
pipeline: &HamelinPipeline,
previous_command: &PendingQuery,
aliasing: FromAliasing,
anonymous: FromAnonymous,
) -> Result<PendingQuery, TranslationErrors> {
let mut merged_env = Environment::default();
let hfc_vec: Vec<HamelinFromClause> = ctx
.iter()
.map(|from| {
HamelinFromClause::new(
from.clone(),
Arc::new(pipeline.cte.clone()),
pipeline.context.clone(),
)
})
.collect();
for from in hfc_vec.iter() {
from.append_completions();
}
let mut table_refs = vec![];
for (hfc, from) in hfc_vec.iter().zip(ctx.iter()) {
let (env, tr, maybe_si) = hfc.reflect()?;
let si = match (maybe_si, &aliasing, &anonymous) {
(Some(_), FromAliasing::RaiseErrorOnAlias(error), _) => {
let err = TranslationError::msg(from.as_ref(), error).single();
return Err(err);
}
(Some(si), FromAliasing::AliasingAllowed, _) => Some(si),
(None, _, FromAnonymous::DefaultAlias) => Some(hfc.table_identifier()?.last().clone()),
(None, _, FromAnonymous::DoNotNest) => None,
};
table_refs.push((tr, si.clone(), env.clone(), from.clone()));
let env_to_merge = match si {
Some(si) => env
.clone()
.with_binding(si.into(), env.fields.clone().into()),
None => env.clone(),
};
merged_env = merged_env.merge(&env_to_merge).map_err(|e| match &e.at {
Some(at) => TranslationError::msg(
from.as_ref(),
format!("Field {} is of type {}.", at.to_hamelin(), e.other).as_str(),
)
.with_source(e),
None => TranslationError::wrap(from.as_ref(), e),
})?;
}
if table_refs.is_empty() {
return Ok(previous_command.clone());
}
let mut queries = vec![];
let (first_table_ref, first_table_alias, _, _) = table_refs.get(0).unwrap();
if table_refs.len() == 1 && first_table_alias.is_none() {
let query = previous_command
.query
.clone()
.from(first_table_ref.clone().into())
.select(
merged_env
.get_column_projections()
.into_iter()
.map(|cp| cp.into())
.collect(),
);
queries.push(query);
} else {
for (table_ref, table_alias, env, from_clause) in table_refs {
let mut projection_builder = ProjectionBuilder::deep_initialize_from_environment(&env);
if let Some(ta) = table_alias {
projection_builder.bind(
ta.into(),
ProjectionBuilder::shallow_initialize_from_environment(&env)
.build_cast()
.map_err(|e| TranslationError::wrap_box(from_clause.as_ref(), e.into()))?
.into(),
env.fields.clone().into(),
);
}
let query = prepend_projections(
&previous_command
.query
.clone()
.from(table_ref.clone().into()),
projection_builder
.expand(merged_env.fields.clone())
.map_err(|e| TranslationError::wrap_box(from_clause.as_ref(), e.into()))?
.build_projections()
.map_err(|e| TranslationError::wrap_box(from_clause.as_ref(), e.into()))?,
&merged_env,
);
queries.push(query);
}
}
let query = if queries.len() == 1 {
queries.pop().unwrap()
} else {
SQLQuery::default()
.from(
SubQuery::new(
queries
.into_iter()
.map(|q| SQLQueryExpression::from(q))
.reduce(|a, b| SetOperation::new(a.into(), b.into()).into())
.unwrap_or_else(|| SQLQuery::default().into())
.into(),
)
.into(),
)
.select(
merged_env
.get_column_projections()
.into_iter()
.map(|cp| cp.into())
.collect(),
)
};
let mut ret_query = query;
if let Some(wf) = pipeline.within.clone() {
if merged_env.lookup(&"timestamp".parse()?).is_ok() {
ret_query = add_filter_condition(&ret_query, wf, &merged_env);
}
}
Ok(PendingQuery::new(ret_query, merged_env))
}