hamelin_legacy 0.4.3

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

use crate::ast::command::{
    agg, append, drop, from, join, let_cmd, limit, parse, select, sort, where_cmd, window, within,
};
use crate::env::Environment;
use crate::translation::{PendingQueryResult, PendingStatementResult};
use hamelin_lib::antlr::hamelinparser::{
    CommandContextAll, FromCommandContextAttrs, PipelineAltContextAttrs, PipelineContextAll,
    UnionCommandContextAttrs,
};
use hamelin_lib::err::TranslationError;
use hamelin_lib::sql::expression::SQLExpression;

use super::command::from::{FromAliasing, FromAnonymous};
use super::command::{explode, match_cmd_cte, nest, unnest};
use super::QueryTranslationContext;

pub struct HamelinPipeline {
    pub tree: Rc<PipelineContextAll<'static>>,
    pub cte: Environment,
    pub context: Rc<QueryTranslationContext>,
    pub within: Option<SQLExpression>,
}

impl HamelinPipeline {
    pub fn new(
        tree: Rc<PipelineContextAll<'static>>,
        cte: Environment,
        context: Rc<QueryTranslationContext>,
    ) -> Self {
        Self {
            tree,
            cte,
            context,
            within: None,
        }
    }

    pub fn translate(&self) -> PendingStatementResult {
        match self.tree.as_ref() {
            PipelineContextAll::PipelineAltContext(ctx) => {
                let mut pending_query_result = PendingQueryResult::default();
                let size = ctx.command_all().len();
                for (i, command) in ctx.command_all().into_iter().enumerate() {
                    match command.as_ref() {
                        CommandContextAll::WhereCommandContext(ctx) => pending_query_result
                            .consume_result(where_cmd::translate(
                                &ctx,
                                &self,
                                &pending_query_result.translation,
                            )),
                        CommandContextAll::LetCommandContext(ctx) => let_cmd::translate(
                                &ctx,
                                &self,
                                &mut pending_query_result,
                            ),
                        CommandContextAll::DropCommandContext(ctx) => pending_query_result
                            .consume_result(drop::translate(
                                &ctx,
                                &pending_query_result.translation,
                            )),
                        CommandContextAll::SortCommandContext(ctx) => pending_query_result
                            .consume_result(sort::translate(
                                &ctx,
                                &self,
                                &pending_query_result.translation,
                            )),
                        CommandContextAll::SelectCommandContext(ctx) => {
                            select::translate(
                                &ctx,
                                &self,
                                &mut pending_query_result,
                            )
                        },
                        CommandContextAll::WindowCommandContext(ctx) => {
                            window::translate(
                                &ctx,
                                &self,
                                &mut pending_query_result,
                            )
                        },
                        CommandContextAll::LimitCommandContext(ctx) => pending_query_result
                            .consume_result(limit::translate(
                                &ctx,
                                &self,
                                &pending_query_result.translation,
                            )),
                        CommandContextAll::WithinCommandContext(ctx) => pending_query_result
                            .consume_result(within::translate(
                                &ctx,
                                &pending_query_result.translation,
                                self.context.clone(),
                            )),
                        CommandContextAll::AggCommandContext(ctx) => {
                            agg::translate(&ctx, &self, &mut pending_query_result)
                        }
                        CommandContextAll::FromCommandContext(ctx) => pending_query_result
                            .consume_result(from::uber_from(
                                ctx.fromClause_all(),
                                &self,
                                &pending_query_result.translation,
                                FromAliasing::AliasingAllowed,
                                FromAnonymous::DoNotNest,
                            )),
                        CommandContextAll::UnionCommandContext(ctx) => pending_query_result.consume_result(from::uber_from(
                            ctx.fromClause_all(),
                            &self,
                            &pending_query_result.translation,
                            FromAliasing::RaiseErrorOnAlias(
                                "Not allowed to assign in UNION. UNION does not support nesting.",
                            ),
                            FromAnonymous::DoNotNest,
                        )),
                        CommandContextAll::ParseCommandContext(ctx) => {
                            pending_query_result.consume_result(parse::translate(
                                &ctx,
                                &self,
                                &pending_query_result.translation,
                            ))
                        }
                        CommandContextAll::JoinCommandContext(base_parser_rule_context) => {
                            pending_query_result.consume_result(join::translate(
                                &base_parser_rule_context,
                                &self,
                                &pending_query_result.translation,
                            ))
                        }
                        CommandContextAll::UnnestCommandContext(base_parser_rule_context) => {
                            pending_query_result.consume_result(unnest::translate(
                                &base_parser_rule_context,
                                &self,
                                &pending_query_result.translation,
                            ))
                        }
                        CommandContextAll::ExplodeCommandContext(base_parser_rule_context) => {
                            pending_query_result.consume_result(explode::translate(
                                &base_parser_rule_context,
                                &self,
                                &pending_query_result.translation,
                            ))
                        }
                        CommandContextAll::MatchCommandContext(base_parser_rule_context) => {
                            pending_query_result.consume_result(match_cmd_cte::translate_with_cte(
                                &base_parser_rule_context,
                                &self,
                                &pending_query_result.translation,
                            ))
                        }
                        CommandContextAll::NestCommandContext(base_parser_rule_context) => {
                            pending_query_result.consume_result(nest::translate(
                                &base_parser_rule_context,
                                &self,
                                &pending_query_result.translation,
                            ))
                        }
                        CommandContextAll::AppendCommandContext(ctx) => {
                            // Append is special. It creates DML, not SQL.
                            // Therefore, no matter what, we bail out here.
                            // That said: before we bail out, we need to make sure that it is in the last position, and that
                            //            the query author wasn't trying to do something weird.
                            if i != size - 1 {
                                pending_query_result.errors.add(TranslationError::msg(
                                    ctx,
                                    "APPEND must be the last command in a pipeline",
                                ));
                            }

                            return match append::translate(
                                &ctx,
                                &self,
                                &pending_query_result.translation,
                            ) {
                                Ok(translation) => PendingStatementResult::default()
                                    .with_translation(translation)
                                    .with_errors(pending_query_result.errors),
                                Err(e) => {
                                    pending_query_result.errors.extend(e);
                                    PendingStatementResult::default()
                                        .with_errors(pending_query_result.errors)
                                }
                            };
                        }
                        CommandContextAll::Error(_) => {
                            pending_query_result.errors.add(TranslationError::msg(
                                ctx,
                                "parse error",
                            ));
                        }
                    };
                }
                if pending_query_result
                    .translation
                    .query
                    .projections
                    .is_empty()
                    && pending_query_result.errors.is_empty()
                {
                    pending_query_result
                        .errors
                        .add(TranslationError::msg(ctx, "query defines no projections"));
                }

                PendingStatementResult::default()
                    .with_translation(pending_query_result.translation.into())
                    .with_errors(pending_query_result.errors)
            }
            PipelineContextAll::Error(ctx) => PendingStatementResult::default()
                .with_error(TranslationError::msg(ctx, "parse error")),
        }
    }

    pub fn contains_within_command(&self) -> bool {
        match self.tree.as_ref() {
            PipelineContextAll::PipelineAltContext(ctx) => {
                for command in ctx.command_all() {
                    match command.as_ref() {
                        CommandContextAll::WithinCommandContext(_) => return true,
                        _ => {}
                    }
                }

                return false;
            }
            PipelineContextAll::Error(_) => {
                return false;
            }
        }
    }

    pub fn with_within(mut self, within: SQLExpression) -> Self {
        self.within = Some(within);
        self
    }

    pub fn add_within_if_none_present(self, within: SQLExpression) -> Self {
        if self.contains_within_command() {
            self
        } else {
            self.with_within(within)
        }
    }
}