clickhouse_orm/
query_builder.rs

1use sea_query::{BinOper, EscapeBuilder, QueryBuilder, QuotedBuilder, SelectDistinct, SqlWriter, SubQueryStatement, TableRefBuilder, Value};
2use sea_query::extension::postgres::PgBinOper;
3use sea_query::Write;
4
5pub struct ClickHouseQueryBuilder;
6
7impl QuotedBuilder for ClickHouseQueryBuilder {
8    fn quote(&self) -> char {
9        '"'
10    }
11}
12
13impl EscapeBuilder for ClickHouseQueryBuilder {}
14
15impl TableRefBuilder for ClickHouseQueryBuilder {}
16
17impl QueryBuilder for ClickHouseQueryBuilder {
18    fn placeholder(&self) -> (&str, bool) {
19        ("$", true)
20    }
21
22    fn prepare_select_distinct(&self, select_distinct: &SelectDistinct, sql: &mut dyn SqlWriter) {
23        match select_distinct {
24            SelectDistinct::All => write!(sql, "ALL").unwrap(),
25            SelectDistinct::Distinct => write!(sql, "DISTINCT").unwrap(),
26            SelectDistinct::DistinctOn(cols) => {
27                write!(sql, "DISTINCT ON (").unwrap();
28                cols.iter().fold(true, |first, column_ref| {
29                    if !first {
30                        write!(sql, ", ").unwrap();
31                    }
32                    self.prepare_column_ref(column_ref, sql);
33                    false
34                });
35                write!(sql, ")").unwrap();
36            }
37            _ => {}
38        };
39    }
40
41    fn prepare_bin_oper(&self, bin_oper: &BinOper, sql: &mut dyn SqlWriter) {
42        match bin_oper {
43            BinOper::PgOperator(oper) => write!(
44                sql,
45                "{}",
46                match oper {
47                    PgBinOper::ILike => "ILIKE",
48                    PgBinOper::NotILike => "NOT ILIKE",
49                    PgBinOper::Matches => "@@",
50                    PgBinOper::Contains => "@>",
51                    PgBinOper::Contained => "<@",
52                    PgBinOper::Concatenate => "||",
53                    PgBinOper::Similarity => "%",
54                    PgBinOper::WordSimilarity => "<%",
55                    PgBinOper::StrictWordSimilarity => "<<%",
56                    PgBinOper::SimilarityDistance => "<->",
57                    PgBinOper::WordSimilarityDistance => "<<->",
58                    PgBinOper::StrictWordSimilarityDistance => "<<<->",
59                }
60            )
61                .unwrap(),
62            _ => self.prepare_bin_oper_common(bin_oper, sql),
63        }
64    }
65
66    fn prepare_query_statement(&self, _: &SubQueryStatement, _: &mut dyn SqlWriter) {}
67
68    // fn prepare_order_expr(&self, order_expr: &OrderExpr, sql: &mut dyn SqlWriter) {
69    //     if !matches!(order_expr.order, Order::Field(_)) {
70    //         self.prepare_simple_expr(&order_expr.expr, sql);
71    //         write!(sql, " ").unwrap();
72    //     }
73    //     self.prepare_order(order_expr, sql);
74    //     match order_expr.nulls {
75    //         None => (),
76    //         Some(NullOrdering::Last) => write!(sql, " NULLS LAST").unwrap(),
77    //         Some(NullOrdering::First) => write!(sql, " NULLS FIRST").unwrap(),
78    //     }
79    // }
80
81    fn prepare_value(&self, value: &Value, sql: &mut dyn SqlWriter) {
82        sql.push_param(value.clone(), self as _);
83    }
84
85    fn write_string_quoted(&self, string: &str, buffer: &mut String) {
86        let escaped = self.escape_string(string);
87        let string = if escaped.find('\\').is_some() {
88            "E'".to_owned() + &escaped + "'"
89        } else {
90            "'".to_owned() + &escaped + "'"
91        };
92        write!(buffer, "{}", string).unwrap()
93    }
94
95    fn if_null_function(&self) -> &str {
96        "COALESCE"
97    }
98}