sea_query/backend/postgres/
query.rs

1use super::*;
2use crate::extension::postgres::*;
3
4impl OperLeftAssocDecider for PostgresQueryBuilder {
5    fn well_known_left_associative(&self, op: &BinOper) -> bool {
6        let common_answer = common_well_known_left_associative(op);
7        let pg_specific_answer = matches!(op, BinOper::PgOperator(PgBinOper::Concatenate));
8        common_answer || pg_specific_answer
9    }
10}
11
12impl PrecedenceDecider for PostgresQueryBuilder {
13    fn inner_expr_well_known_greater_precedence(
14        &self,
15        inner: &SimpleExpr,
16        outer_oper: &Oper,
17    ) -> bool {
18        let common_answer = common_inner_expr_well_known_greater_precedence(inner, outer_oper);
19        let pg_specific_answer = match inner {
20            SimpleExpr::Binary(_, inner_bin_oper, _) => {
21                let inner_oper: Oper = (*inner_bin_oper).into();
22                if inner_oper.is_arithmetic() || inner_oper.is_shift() {
23                    is_ilike(inner_bin_oper)
24                } else if is_pg_comparison(inner_bin_oper) {
25                    outer_oper.is_logical()
26                } else {
27                    false
28                }
29            }
30            _ => false,
31        };
32        common_answer || pg_specific_answer
33    }
34}
35
36impl QueryBuilder for PostgresQueryBuilder {
37    fn placeholder(&self) -> (&str, bool) {
38        ("$", true)
39    }
40
41    fn prepare_simple_expr(&self, simple_expr: &SimpleExpr, sql: &mut dyn SqlWriter) {
42        match simple_expr {
43            SimpleExpr::AsEnum(type_name, expr) => {
44                let simple_expr = expr.clone().cast_as(SeaRc::clone(type_name));
45                self.prepare_simple_expr_common(&simple_expr, sql);
46            }
47            _ => QueryBuilder::prepare_simple_expr_common(self, simple_expr, sql),
48        }
49    }
50
51    fn prepare_select_distinct(&self, select_distinct: &SelectDistinct, sql: &mut dyn SqlWriter) {
52        match select_distinct {
53            SelectDistinct::All => write!(sql, "ALL").unwrap(),
54            SelectDistinct::Distinct => write!(sql, "DISTINCT").unwrap(),
55            SelectDistinct::DistinctOn(cols) => {
56                write!(sql, "DISTINCT ON (").unwrap();
57                cols.iter().fold(true, |first, column_ref| {
58                    if !first {
59                        write!(sql, ", ").unwrap();
60                    }
61                    self.prepare_column_ref(column_ref, sql);
62                    false
63                });
64                write!(sql, ")").unwrap();
65            }
66            _ => {}
67        };
68    }
69
70    fn prepare_bin_oper(&self, bin_oper: &BinOper, sql: &mut dyn SqlWriter) {
71        match bin_oper {
72            BinOper::PgOperator(oper) => write!(
73                sql,
74                "{}",
75                match oper {
76                    PgBinOper::ILike => "ILIKE",
77                    PgBinOper::NotILike => "NOT ILIKE",
78                    PgBinOper::Matches => "@@",
79                    PgBinOper::Contains => "@>",
80                    PgBinOper::Contained => "<@",
81                    PgBinOper::Concatenate => "||",
82                    PgBinOper::Overlap => "&&",
83                    PgBinOper::Similarity => "%",
84                    PgBinOper::WordSimilarity => "<%",
85                    PgBinOper::StrictWordSimilarity => "<<%",
86                    PgBinOper::SimilarityDistance => "<->",
87                    PgBinOper::WordSimilarityDistance => "<<->",
88                    PgBinOper::StrictWordSimilarityDistance => "<<<->",
89                    PgBinOper::GetJsonField => "->",
90                    PgBinOper::CastJsonField => "->>",
91                    PgBinOper::Regex => "~",
92                    PgBinOper::RegexCaseInsensitive => "~*",
93                    #[cfg(feature = "postgres-vector")]
94                    PgBinOper::EuclideanDistance => "<->",
95                    #[cfg(feature = "postgres-vector")]
96                    PgBinOper::NegativeInnerProduct => "<#>",
97                    #[cfg(feature = "postgres-vector")]
98                    PgBinOper::CosineDistance => "<=>",
99                }
100            )
101            .unwrap(),
102            _ => self.prepare_bin_oper_common(bin_oper, sql),
103        }
104    }
105
106    fn prepare_query_statement(&self, query: &SubQueryStatement, sql: &mut dyn SqlWriter) {
107        query.prepare_statement(self, sql);
108    }
109
110    fn prepare_function_name(&self, function: &Function, sql: &mut dyn SqlWriter) {
111        match function {
112            Function::PgFunction(function) => write!(
113                sql,
114                "{}",
115                match function {
116                    PgFunction::ToTsquery => "TO_TSQUERY",
117                    PgFunction::ToTsvector => "TO_TSVECTOR",
118                    PgFunction::PhrasetoTsquery => "PHRASETO_TSQUERY",
119                    PgFunction::PlaintoTsquery => "PLAINTO_TSQUERY",
120                    PgFunction::WebsearchToTsquery => "WEBSEARCH_TO_TSQUERY",
121                    PgFunction::TsRank => "TS_RANK",
122                    PgFunction::TsRankCd => "TS_RANK_CD",
123                    PgFunction::StartsWith => "STARTS_WITH",
124                    PgFunction::GenRandomUUID => "GEN_RANDOM_UUID",
125                    PgFunction::JsonBuildObject => "JSON_BUILD_OBJECT",
126                    PgFunction::JsonAgg => "JSON_AGG",
127                    PgFunction::ArrayAgg => "ARRAY_AGG",
128                    PgFunction::DateTrunc => "DATE_TRUNC",
129                    #[cfg(feature = "postgres-array")]
130                    PgFunction::Any => "ANY",
131                    #[cfg(feature = "postgres-array")]
132                    PgFunction::Some => "SOME",
133                    #[cfg(feature = "postgres-array")]
134                    PgFunction::All => "ALL",
135                }
136            )
137            .unwrap(),
138            _ => self.prepare_function_name_common(function, sql),
139        }
140    }
141
142    fn prepare_order_expr(&self, order_expr: &OrderExpr, sql: &mut dyn SqlWriter) {
143        if !matches!(order_expr.order, Order::Field(_)) {
144            self.prepare_simple_expr(&order_expr.expr, sql);
145        }
146        self.prepare_order(order_expr, sql);
147        match order_expr.nulls {
148            None => (),
149            Some(NullOrdering::Last) => write!(sql, " NULLS LAST").unwrap(),
150            Some(NullOrdering::First) => write!(sql, " NULLS FIRST").unwrap(),
151        }
152    }
153
154    fn prepare_value(&self, value: &Value, sql: &mut dyn SqlWriter) {
155        sql.push_param(value.clone(), self as _);
156    }
157
158    fn write_string_quoted(&self, string: &str, buffer: &mut String) {
159        let escaped = self.escape_string(string);
160        let string = if escaped.find('\\').is_some() {
161            "E'".to_owned() + &escaped + "'"
162        } else {
163            "'".to_owned() + &escaped + "'"
164        };
165        write!(buffer, "{string}").unwrap()
166    }
167
168    fn write_bytes(&self, bytes: &[u8], buffer: &mut String) {
169        write!(buffer, "'\\x").unwrap();
170        for b in bytes {
171            write!(buffer, "{b:02X}").unwrap();
172        }
173        write!(buffer, "'").unwrap();
174    }
175
176    fn if_null_function(&self) -> &str {
177        "COALESCE"
178    }
179}
180
181fn is_pg_comparison(b: &BinOper) -> bool {
182    matches!(
183        b,
184        BinOper::PgOperator(PgBinOper::Contained)
185            | BinOper::PgOperator(PgBinOper::Contains)
186            | BinOper::PgOperator(PgBinOper::Similarity)
187            | BinOper::PgOperator(PgBinOper::WordSimilarity)
188            | BinOper::PgOperator(PgBinOper::StrictWordSimilarity)
189            | BinOper::PgOperator(PgBinOper::Matches)
190    )
191}
192
193fn is_ilike(b: &BinOper) -> bool {
194    matches!(
195        b,
196        BinOper::PgOperator(PgBinOper::ILike) | BinOper::PgOperator(PgBinOper::NotILike)
197    )
198}