hamelin_translation 0.9.7

Lowering and IR for Hamelin query language
Documentation
//! Helper for lowering compound assignments to simple identifiers.
//!
//! Used by AGG and WINDOW command normalizers to handle compound identifiers
//! like `x.y = sum(value)` by generating flat identifiers + SET/DROP restoration.

use std::sync::Arc;

use hamelin_lib::tree::{
    ast::{
        command::Command,
        expression::Expression,
        identifier::{Identifier, SimpleIdentifier},
    },
    builder::{drop_command, field_ref, set_command},
    typed_ast::{clause::Projections, environment::TypeEnvironment},
};

pub use crate::unique::UniqueNameGenerator;

/// Lower compound assignments in projections to simple identifiers.
///
/// For each compound identifier (e.g., `x.y = expr`), generates:
/// - A simple assignment (`__flat_0 = expr`) returned in the first tuple element
/// - Restore commands (SET + DROP) returned in the second tuple element
///
/// Simple identifiers pass through unchanged with no restore commands.
///
/// The `schema` parameter is used to ensure generated names don't collide with
/// existing columns.
pub fn lower_compound_assignments(
    projections: &Projections,
    name_gen: &mut UniqueNameGenerator,
    schema: &TypeEnvironment,
) -> (Vec<(SimpleIdentifier, Arc<Expression>)>, Vec<Command>) {
    let mut assignments = Vec::new();
    let mut set_builder = set_command();
    let mut drop_builder = drop_command();
    let mut has_compounds = false;

    for assignment in &projections.assignments {
        match assignment.identifier.valid_ref() {
            Ok(Identifier::Compound(compound)) => {
                // Compound identifier - lower it
                let flat_name = name_gen.next(schema);
                assignments.push((flat_name.clone(), assignment.expression.ast.clone()));

                // Build restore: SET x.y = __flat_0
                let original: Identifier = compound.clone().into();
                set_builder = set_builder.named_field(original, field_ref(flat_name.as_str()));

                // Build cleanup: DROP __flat_0
                drop_builder = drop_builder.field(flat_name);

                has_compounds = true;
            }
            Ok(Identifier::Simple(simple)) => {
                // Simple identifier - pass through unchanged
                assignments.push((simple.clone(), assignment.expression.ast.clone()));
            }
            Err(_) => {
                // Error identifier - skip (already type checked away)
            }
        }
    }

    if has_compounds {
        (assignments, vec![set_builder.build(), drop_builder.build()])
    } else {
        (assignments, vec![])
    }
}