1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! 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 crate::{ast::rq::Query, Options, PRQL_VERSION};

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

/// Translate a PRQL AST into a SQL string.
pub fn compile(query: Query, options: Options) -> Result<String> {
    let crate::Target::Sql(dialect) = options.target;
    let sql_ast = gen_query::translate_query(query, dialect.clone())?;

    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("} }", "}}") + "\n"
    } else {
        sql
    };

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

    Ok(sql)
}

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,
}

#[cfg(test)]
mod test {
    use crate::compile;
    use crate::Options;

    #[test]
    fn test_end_with_new_line() {
        let sql = compile("from a", Options::default().no_signature()).unwrap();
        assert_eq!(sql, "SELECT\n  *\nFROM\n  a\n")
    }
}