prql-compiler 0.4.2

PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement.
Documentation
//! Backend for translating RQ into SQL

mod anchor;
mod context;
mod dialect;
mod gen_expr;
mod gen_projection;
mod gen_query;
mod preprocess;
mod std;

pub use dialect::Dialect;

use anyhow::Result;
use serde::{Deserialize, Serialize};

use crate::{ast::rq::Query, PRQL_VERSION};

use self::{context::AnchorContext, dialect::DialectHandler};

/// Translate a PRQL AST into a SQL string.
pub fn compile(query: Query, options: Option<Options>) -> Result<String> {
    let options = options.unwrap_or_default();

    let sql_ast = gen_query::translate_query(query, options.dialect)?;

    let sql = sql_ast.to_string();

    // formatting
    let sql = if options.format {
        let formatted = sqlformat::format(
            &sql,
            &sqlformat::QueryParams::default(),
            sqlformat::FormatOptions::default(),
        );

        // The sql formatter turns `{{` into `{ {`, and while that's reasonable SQL,
        // we want to allow jinja expressions through. So we (somewhat hackily) replace
        // any `{ {` with `{{`.
        formatted.replace("{ {", "{{").replace("} }", "}}")
    } else {
        sql
    };

    // signature
    let sql = if options.signature_comment {
        let pre = if options.format { "\n\n" } else { " " };
        let post = if options.format { "\n" } else { "" };
        let signature = format!(
            "{pre}-- Generated by PRQL compiler version {} (https://prql-lang.org){post}",
            *PRQL_VERSION
        );
        sql + &signature
    } else {
        sql
    };

    Ok(sql)
}

/// Compilation options for SQL backend of the compiler.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Options {
    /// Pass generated SQL string trough a formatter that splits it
    /// into multiple lines and prettifies indentation and spacing.
    ///
    /// Defaults to true.
    pub format: bool,

    /// Target dialect you want to compile for.
    ///
    /// Because PRQL compiles to a subset of SQL, not all SQL features are
    /// required for PRQL. This means that generic dialect may work with most
    /// databases.
    ///
    /// If something does not work in dialect you need, please report it at
    /// GitHub issues.
    ///
    /// If None is used, `sql_dialect` flag from query definition is used.
    /// If it does not exist, [Dialect::Generic] is used.
    pub dialect: Option<Dialect>,

    /// Emits the compiler signature as a comment after generated SQL
    ///
    /// Defaults to true.
    pub signature_comment: bool,
}

impl Default for Options {
    fn default() -> Self {
        Self {
            format: true,
            dialect: None,
            signature_comment: true,
        }
    }
}

impl Options {
    pub fn no_format(mut self) -> Self {
        self.format = false;
        self
    }

    pub fn no_signature(mut self) -> Self {
        self.signature_comment = false;
        self
    }

    pub fn with_dialect(mut self, dialect: Dialect) -> Self {
        self.dialect = Some(dialect);
        self
    }

    pub fn some(self) -> Option<Self> {
        Some(self)
    }
}

struct Context {
    pub dialect: Box<dyn DialectHandler>,
    pub anchor: AnchorContext,

    pub omit_ident_prefix: bool,

    /// True iff codegen should generate expressions before SELECT's projection is applied.
    /// For example:
    /// - WHERE needs `pre_projection=true`, but
    /// - ORDER BY needs `pre_projection=false`.
    pub pre_projection: bool,
}