sea_query/backend/postgres/
query.rs1use 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_table_sample(&self, select: &SelectStatement, sql: &mut dyn SqlWriter) {
143 let Some(table_sample) = select.table_sample else {
144 return;
145 };
146
147 match table_sample.method {
148 SampleMethod::BERNOULLI => write!(sql, " TABLESAMPLE BERNOULLI").unwrap(),
149 SampleMethod::SYSTEM => write!(sql, " TABLESAMPLE SYSTEM").unwrap(),
150 }
151 write!(sql, " ({})", table_sample.percentage).unwrap();
152 if let Some(repeatable) = table_sample.repeatable {
153 write!(sql, " REPEATABLE ({repeatable})").unwrap();
154 }
155 }
156
157 fn prepare_order_expr(&self, order_expr: &OrderExpr, sql: &mut dyn SqlWriter) {
158 if !matches!(order_expr.order, Order::Field(_)) {
159 self.prepare_simple_expr(&order_expr.expr, sql);
160 }
161 self.prepare_order(order_expr, sql);
162 match order_expr.nulls {
163 None => (),
164 Some(NullOrdering::Last) => write!(sql, " NULLS LAST").unwrap(),
165 Some(NullOrdering::First) => write!(sql, " NULLS FIRST").unwrap(),
166 }
167 }
168
169 fn prepare_value(&self, value: &Value, sql: &mut dyn SqlWriter) {
170 sql.push_param(value.clone(), self as _);
171 }
172
173 fn write_string_quoted(&self, string: &str, buffer: &mut String) {
174 let escaped = self.escape_string(string);
175 let string = if escaped.find('\\').is_some() {
176 "E'".to_owned() + &escaped + "'"
177 } else {
178 "'".to_owned() + &escaped + "'"
179 };
180 write!(buffer, "{string}").unwrap()
181 }
182
183 fn write_bytes(&self, bytes: &[u8], buffer: &mut String) {
184 write!(buffer, "'\\x").unwrap();
185 for b in bytes {
186 write!(buffer, "{b:02X}").unwrap();
187 }
188 write!(buffer, "'").unwrap();
189 }
190
191 fn if_null_function(&self) -> &str {
192 "COALESCE"
193 }
194}
195
196fn is_pg_comparison(b: &BinOper) -> bool {
197 matches!(
198 b,
199 BinOper::PgOperator(PgBinOper::Contained)
200 | BinOper::PgOperator(PgBinOper::Contains)
201 | BinOper::PgOperator(PgBinOper::Similarity)
202 | BinOper::PgOperator(PgBinOper::WordSimilarity)
203 | BinOper::PgOperator(PgBinOper::StrictWordSimilarity)
204 | BinOper::PgOperator(PgBinOper::Matches)
205 )
206}
207
208fn is_ilike(b: &BinOper) -> bool {
209 matches!(
210 b,
211 BinOper::PgOperator(PgBinOper::ILike) | BinOper::PgOperator(PgBinOper::NotILike)
212 )
213}