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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! 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,
}