use anyhow::bail;
use hamelin_lib::func::def::ParameterBinding;
use hamelin_lib::sql::expression::apply::{
BinaryOperatorApply, FunctionCallApply, Lambda, UnaryOperatorApply,
};
use hamelin_lib::sql::expression::literal::{BooleanLiteral, IntegerLiteral, StringLiteral};
use hamelin_lib::sql::expression::operator::Operator;
use hamelin_lib::sql::expression::IndexLookup;
use hamelin_lib::sql::expression::{Leaf, SQLExpression};
use hamelin_lib::translation::ExpressionTranslation;
use hamelin_lib::types::TIMESTAMP;
use crate::range_builder::RangeBuilder;
pub fn direct_function_translation(
func: &str,
bindings: ParameterBinding<ExpressionTranslation>,
) -> anyhow::Result<SQLExpression> {
let all_args = bindings.into_iter().map(|t| t.sql).collect();
let fca: SQLExpression = FunctionCallApply::with_positional(func, all_args).into();
Ok(fca)
}
pub fn function_translation_respecting_ignore_nulls(
func: &str,
mut bindings: ParameterBinding<ExpressionTranslation>,
) -> anyhow::Result<SQLExpression> {
let ignore_nulls = bindings.take_by_name("ignore_nulls")?;
let all_args = bindings.into_iter().map(|t| t.sql).collect();
let mut fca = FunctionCallApply::with_positional(func, all_args);
match ignore_nulls.sql {
SQLExpression::Leaf(Leaf::BooleanLiteral(b)) => {
if b.value {
fca = fca.with_ignore_nulls();
}
}
_ => bail!("ignore_nulls must be boolean literal"),
}
Ok(fca.into())
}
pub fn direct_unary_operator_translation(
func: &str,
mut bindings: ParameterBinding<ExpressionTranslation>,
) -> anyhow::Result<SQLExpression> {
let sql_op: Operator = hamelin_lib::operator::Operator::of(func)?.try_into()?;
Ok(UnaryOperatorApply::new(sql_op, bindings.take()?.sql).into())
}
pub fn direct_binary_operator_translation(
func: &str,
mut bindings: ParameterBinding<ExpressionTranslation>,
) -> anyhow::Result<SQLExpression> {
let left = bindings.take()?.sql;
let right = bindings.take()?.sql;
let sql_op: Operator = hamelin_lib::operator::Operator::of(func)?.try_into()?;
Ok(BinaryOperatorApply::new(sql_op, left, right).into())
}
pub fn to_sql_concat(
_: &str,
mut bindings: ParameterBinding<ExpressionTranslation>,
) -> anyhow::Result<SQLExpression> {
let p0 = bindings.take()?;
let p1 = bindings.take()?;
Ok(BinaryOperatorApply::new(Operator::Concat, p0.sql, p1.sql).into())
}
pub fn interval_to_range(interval: SQLExpression) -> RangeBuilder {
RangeBuilder::default()
.with_begin(
interval_to_timestamp(interval),
TIMESTAMP.to_sql().unwrap().into(),
)
.with_end(
FunctionCallApply::with_no_arguments("now").into(),
TIMESTAMP.to_sql().unwrap().into(),
)
}
pub fn timestamp_to_range(timestamp: SQLExpression) -> RangeBuilder {
RangeBuilder::default()
.with_begin(timestamp, TIMESTAMP.to_sql().unwrap().into())
.with_end(
FunctionCallApply::with_no_arguments("now").into(),
TIMESTAMP.to_sql().unwrap().into(),
)
}
pub fn interval_to_timestamp(interval: SQLExpression) -> SQLExpression {
BinaryOperatorApply::new(
Operator::Plus,
FunctionCallApply::with_no_arguments("now").into(),
interval,
)
.into()
}
pub fn interval_range_to_timestamp_range(range_expr: SQLExpression) -> RangeBuilder {
RangeBuilder::from_literal(range_expr.clone())
.map(|rb| {
let mut rb = rb;
if let SQLExpression::Leaf(Leaf::NullLiteral(_)) = &(rb.begin) {
} else {
rb = rb.clone().with_begin(
interval_to_timestamp(rb.begin.clone()),
TIMESTAMP.to_sql().unwrap(),
);
}
if let SQLExpression::Leaf(Leaf::NullLiteral(_)) = rb.end {
} else {
rb = rb.clone().with_end(
interval_to_timestamp(rb.end.clone()),
TIMESTAMP.to_sql().unwrap(),
);
}
rb
})
.unwrap_or(
RangeBuilder::default()
.with_begin(
interval_to_timestamp(
range_expr.clone().index(IntegerLiteral::new("1").into()),
),
TIMESTAMP.to_sql().unwrap().into(),
)
.with_end(
interval_to_timestamp(
range_expr.clone().index(IntegerLiteral::new("2").into()),
),
TIMESTAMP.to_sql().unwrap().into(),
),
)
}
pub fn within_range(left: SQLExpression, range: RangeBuilder) -> SQLExpression {
BinaryOperatorApply::new(
Operator::And,
if let SQLExpression::Leaf(Leaf::NullLiteral(_)) = range.begin {
BooleanLiteral::new(true).into()
} else {
BinaryOperatorApply::new(Operator::Gte, left.clone(), range.begin).into()
},
if let SQLExpression::Leaf(Leaf::NullLiteral(_)) = range.end {
BooleanLiteral::new(true).into()
} else {
BinaryOperatorApply::new(Operator::Lte, left, range.end).into()
},
)
.into()
}
pub fn within_range_expr(left: SQLExpression, right: SQLExpression) -> SQLExpression {
BinaryOperatorApply::new(
Operator::And,
FunctionCallApply::with_two(
"coalesce",
BinaryOperatorApply::new(
Operator::Gte,
left.clone(),
IndexLookup::new(right.clone(), IntegerLiteral::new("1").into()).into(),
)
.into(),
BooleanLiteral::new(true).into(),
)
.into(),
FunctionCallApply::with_two(
"coalesce",
BinaryOperatorApply::new(
Operator::Lt,
left,
IndexLookup::new(right.clone(), IntegerLiteral::new("2").into()).into(),
)
.into(),
BooleanLiteral::new(true).into(),
)
.into(),
)
.into()
}
pub fn convert_nested_transforms(original: SQLExpression, to_function: &str) -> SQLExpression {
if let SQLExpression::FunctionCallApply(fca) = &original {
if fca.function_name == "transform" {
if let Some(SQLExpression::Lambda(lmbda)) = fca.arguments.get(1) {
return FunctionCallApply::with_two(
to_function,
fca.arguments.first().unwrap().clone(),
Lambda::new(
lmbda.arguments.clone(),
convert_nested_transforms(lmbda.body.as_ref().clone(), to_function),
)
.into(),
)
.into();
}
}
}
original
}
pub fn wrap_timestamp(expr: SQLExpression) -> SQLExpression {
FunctionCallApply::with_one(
"try",
FunctionCallApply::with_one(
"from_iso8601_timestamp",
FunctionCallApply::with_three(
"replace",
expr,
StringLiteral::new(" ").into(),
StringLiteral::new("T").into(),
)
.into(),
)
.into(),
)
.into()
}
pub fn array_length(array_expr: SQLExpression) -> SQLExpression {
FunctionCallApply::with_one("cardinality", array_expr).into()
}
pub fn string_length(string_expr: SQLExpression) -> SQLExpression {
FunctionCallApply::with_one("length", string_expr).into()
}
fn negative_index_to_positive(
index_expr: SQLExpression,
length_expr: SQLExpression,
) -> SQLExpression {
FunctionCallApply::with_three(
"if",
BinaryOperatorApply::new(
Operator::Lt,
index_expr.clone(),
IntegerLiteral::new("0").into(),
)
.into(),
BinaryOperatorApply::new(Operator::Plus, length_expr, index_expr.clone()).into(),
index_expr,
)
.into()
}
pub fn array_negative_index_to_positive(
index_expr: SQLExpression,
array_expr: SQLExpression,
) -> SQLExpression {
negative_index_to_positive(index_expr.clone(), array_length(array_expr))
}
pub fn string_negative_index_to_positive(
index_expr: SQLExpression,
string_expr: SQLExpression,
) -> SQLExpression {
negative_index_to_positive(index_expr.clone(), string_length(string_expr))
}
pub fn hamelin_array_index_to_sql_with_negatives(
index_expr: SQLExpression,
array_expr: SQLExpression,
) -> SQLExpression {
let positive_index = array_negative_index_to_positive(index_expr.clone(), array_expr);
BinaryOperatorApply::new(
Operator::Plus,
positive_index,
IntegerLiteral::new("1").into(),
)
.into()
}
pub fn hamelin_string_index_to_sql_with_negatives(
index_expr: SQLExpression,
string_expr: SQLExpression,
) -> SQLExpression {
let positive_index = string_negative_index_to_positive(index_expr.clone(), string_expr);
BinaryOperatorApply::new(
Operator::Plus,
positive_index,
IntegerLiteral::new("1").into(),
)
.into()
}