use std::rc::Rc;
use crate::ast::expression::HamelinExpression;
use crate::ast::QueryTranslationContext;
use crate::translation::sql_query_helpers::add_filter_condition;
use crate::translation::PendingQuery;
use hamelin_lib::antlr::hamelinparser::{WithinCommandContext, WithinCommandContextAttrs};
use hamelin_lib::err::{TranslationError, TranslationErrors};
use hamelin_lib::sql::expression::identifier::SimpleIdentifier;
use hamelin_lib::sql::expression::literal::ColumnReference;
use hamelin_lib::types::Type;
use hamelin_sql::range_builder::RangeBuilder;
use hamelin_sql::utils::{
interval_range_to_timestamp_range, interval_to_range, timestamp_to_range, within_range,
within_range_expr,
};
pub fn translate(
ctx: &WithinCommandContext<'static>,
previous: &PendingQuery,
translation_context: Rc<QueryTranslationContext>,
) -> Result<PendingQuery, TranslationErrors> {
let exp_tree = TranslationErrors::expect(ctx, ctx.expression())?;
let exp = HamelinExpression::new(
exp_tree.clone(),
translation_context.default_expression_translation_context(&previous.env),
);
let translation = exp.translate()?;
if let Err(e) = previous
.env
.lookup(&SimpleIdentifier::new("timestamp").into())
{
let e = TranslationError::msg(
ctx,
"Cannot use WITHIN here. The environment is missing the timestamp column.",
)
.with_source(e)
.single();
return Err(e);
}
let col_ref = ColumnReference::new(SimpleIdentifier::new("timestamp").into()).into();
let res = match translation.typ {
Type::Timestamp => PendingQuery::new(
add_filter_condition(
&previous.query,
within_range(col_ref, timestamp_to_range(translation.sql)),
&previous.env,
),
previous.env.clone(),
),
Type::Interval | Type::CalendarInterval => PendingQuery::new(
add_filter_condition(
&previous.query,
within_range(col_ref, interval_to_range(translation.sql)),
&previous.env,
),
previous.env.clone(),
),
Type::Range(r) => match r.of.as_ref() {
Type::Interval | Type::CalendarInterval => PendingQuery::new(
add_filter_condition(
&previous.query,
within_range(col_ref, interval_range_to_timestamp_range(translation.sql)),
&previous.env,
),
previous.env.clone(),
),
Type::Timestamp => PendingQuery::new(
add_filter_condition(
&previous.query,
RangeBuilder::from_literal(translation.sql.clone())
.map(|r| within_range(col_ref.clone(), r))
.unwrap_or_else(|| within_range_expr(col_ref, translation.sql)),
&previous.env,
),
previous.env.clone(),
),
t => {
return Err(TranslationError::msg(
ctx,
"WITHIN range must be of timestamp or interval",
)
.with_context(exp_tree.as_ref(), &format!("found: {}", t))
.single())
}
},
t => {
return Err(
TranslationError::msg(ctx, "WITHIN must be range, timestamp, or interval")
.with_context(exp_tree.as_ref(), &format!("found: {}", t))
.single(),
)
}
};
Ok(res)
}