hamelin_legacy 0.3.10

Legacy AST translation code for Hamelin (to be deprecated)
Documentation
use std::rc::Rc;

use crate::ast::expression::HamelinExpression;
use crate::ast::pipeline::HamelinPipeline;
use crate::env::Environment;
use crate::translation::{PendingStatement, PendingStatementResult};
use hamelin_lib::antlr::hamelinparser::{
    ExpressionQueryContextAttrs, QueryContextAll, StandaloneQueryContextAttrs,
    WithQueryContextAttrs,
};
use hamelin_lib::err::TranslationError;
use hamelin_lib::sql::query::cte::CTE;
use hamelin_lib::sql::query::dml::DML;
use hamelin_lib::sql::query::projection::Binding;
use hamelin_lib::sql::query::SQLQuery;
use hamelin_lib::sql::statement::Statement;
use hamelin_lib::types::struct_type::Struct;

use super::QueryTranslationContext;
use hamelin_lib::sql::expression::identifier::HamelinSimpleIdentifier;

pub struct HamelinQuery {
    pub tree: Rc<QueryContextAll<'static>>,
    pub context: Rc<QueryTranslationContext>,
}

impl HamelinQuery {
    pub fn new(tree: Rc<QueryContextAll<'static>>, context: Rc<QueryTranslationContext>) -> Self {
        Self { tree, context }
    }

    pub fn translate(&self) -> PendingStatementResult {
        match self.tree.as_ref() {
            QueryContextAll::WithQueryContext(ctx) => {
                let mut cte = CTE::default();
                let mut env = Environment::default();
                let mut pending = PendingStatementResult::default();

                for (ident_ctx, pipeline_ctx) in ctx
                    .simpleIdentifier_all()
                    .into_iter()
                    .zip(ctx.pipeline_all().into_iter())
                {
                    let maybe_ident = HamelinSimpleIdentifier::new(ident_ctx).to_sql();
                    let mut pipeline = HamelinPipeline::new(
                        pipeline_ctx.clone(),
                        env.clone(),
                        self.context.clone(),
                    );

                    if let Some(within) = self.context.within.clone() {
                        pipeline = pipeline.add_within_if_none_present(within);
                    }

                    let translation = pending.recover(pipeline.translate());

                    match (maybe_ident, translation.statement) {
                        (Ok(i), Statement::SQLQuery(sql)) => {
                            cte = cte.with(i.clone(), sql);
                            env = env.with_binding(i.into(), translation.env.fields.into());
                        }
                        (_, Statement::DML(_)) => {
                            pending.add_error(TranslationError::msg(
                                pipeline_ctx.as_ref(),
                                "DML not allowed in CTE pipeline.",
                            ));
                        }
                        (Err(e), _) => {
                            pending.add_errors(e);
                        }
                    }
                }

                if let Some(pipeline_ctx) = ctx.pipeline_all().last() {
                    let mut pipeline =
                        HamelinPipeline::new(pipeline_ctx.clone(), env, self.context.clone());

                    if let Some(within) = self.context.within.clone() {
                        pipeline = pipeline.add_within_if_none_present(within);
                    }

                    let mut translation = pending.recover(pipeline.translate());
                    // Merge CTEs: top-level WITH clauses come first, then any CTEs
                    // generated by the pipeline (e.g., MATCH CTE strategy)
                    translation = match &translation.statement {
                        Statement::SQLQuery(q) => match cte.merge(q.cte.clone()) {
                            Ok(merged_cte) => PendingStatement::new_query(
                                q.clone().cte(merged_cte),
                                translation.env,
                            ),
                            Err(e) => {
                                pending.add_error(TranslationError::msg(
                                    pipeline_ctx.as_ref(),
                                    &e.to_string(),
                                ));
                                translation
                            }
                        },
                        Statement::DML(DML::Insert(i)) => match cte.merge(i.query.cte.clone()) {
                            Ok(merged_cte) => {
                                let mut i = i.clone();
                                i.query = i.query.clone().cte(merged_cte);
                                PendingStatement::new_dml(i.into(), translation.env)
                            }
                            Err(e) => {
                                pending.add_error(TranslationError::msg(
                                    pipeline_ctx.as_ref(),
                                    &e.to_string(),
                                ));
                                translation
                            }
                        },
                        Statement::DML(DML::Merge(m)) => match cte.merge(m.query.cte.clone()) {
                            Ok(merged_cte) => {
                                let mut m = m.clone();
                                m.query = m.query.clone().cte(merged_cte);
                                PendingStatement::new_dml(m.into(), translation.env)
                            }
                            Err(e) => {
                                pending.add_error(TranslationError::msg(
                                    pipeline_ctx.as_ref(),
                                    &e.to_string(),
                                ));
                                translation
                            }
                        },
                    };
                    pending.translation = translation
                }

                pending
            }
            QueryContextAll::StandaloneQueryContext(ctx) => ctx
                .pipeline()
                .map(|ctx| {
                    let mut p =
                        HamelinPipeline::new(ctx, Environment::default(), self.context.clone());

                    if let Some(within) = self.context.within.clone() {
                        p = p.add_within_if_none_present(within);
                    }

                    p.translate()
                })
                .unwrap_or_default(),

            QueryContextAll::ExpressionQueryContext(ctx) => ctx
                .expression()
                .map(|ectx| {
                    let env = Environment::default();
                    let expression = HamelinExpression::new(
                        ectx,
                        self.context.default_expression_translation_context(&env),
                    );
                    match expression.translate() {
                        Ok(exp) => {
                            let typ = Struct::default().with("result".parse().unwrap(), exp.typ);
                            let pending_statement = PendingStatement::new_query(
                                SQLQuery::default().select(vec![Binding::new(
                                    "result".parse().unwrap(),
                                    exp.sql,
                                )
                                .into()]),
                                Environment::new(typ),
                            );

                            PendingStatementResult::default().with_translation(pending_statement)
                        }
                        Err(e) => PendingStatementResult::default().with_errors(e),
                    }
                })
                .unwrap_or_default(),
            QueryContextAll::Error(ctx) => PendingStatementResult::default()
                .with_error(TranslationError::msg(ctx, "parse error")),
        }
    }
}