Skip to main content

sqlparser/dialect/
postgresql.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18// Licensed under the Apache License, Version 2.0 (the "License");
19// you may not use this file except in compliance with the License.
20// You may obtain a copy of the License at
21//
22// http://www.apache.org/licenses/LICENSE-2.0
23//
24// Unless required by applicable law or agreed to in writing, software
25// distributed under the License is distributed on an "AS IS" BASIS,
26// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27// See the License for the specific language governing permissions and
28// limitations under the License.
29use log::debug;
30
31use crate::dialect::{Dialect, Precedence};
32use crate::keywords::Keyword;
33use crate::parser::{Parser, ParserError};
34use crate::tokenizer::Token;
35
36use super::keywords::RESERVED_FOR_IDENTIFIER;
37
38/// A [`Dialect`] for [PostgreSQL](https://www.postgresql.org/)
39#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41pub struct PostgreSqlDialect {}
42
43const PERIOD_PREC: u8 = 200;
44const DOUBLE_COLON_PREC: u8 = 140;
45const BRACKET_PREC: u8 = 130;
46const COLLATE_PREC: u8 = 120;
47const AT_TZ_PREC: u8 = 110;
48const CARET_PREC: u8 = 100;
49const MUL_DIV_MOD_OP_PREC: u8 = 90;
50const PLUS_MINUS_PREC: u8 = 80;
51// there's no XOR operator in PostgreSQL, but support it here to avoid breaking tests
52const XOR_PREC: u8 = 75;
53const PG_OTHER_PREC: u8 = 70;
54const BETWEEN_LIKE_PREC: u8 = 60;
55const EQ_PREC: u8 = 50;
56const IS_PREC: u8 = 40;
57const NOT_PREC: u8 = 30;
58const AND_PREC: u8 = 20;
59const OR_PREC: u8 = 10;
60
61impl Dialect for PostgreSqlDialect {
62    fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
63        Some('"')
64    }
65
66    fn is_delimited_identifier_start(&self, ch: char) -> bool {
67        ch == '"' // Postgres does not support backticks to quote identifiers
68    }
69
70    fn is_identifier_start(&self, ch: char) -> bool {
71        ch.is_alphabetic() || ch == '_' ||
72        // PostgreSQL implements Unicode characters in identifiers.
73        !ch.is_ascii()
74    }
75
76    fn is_identifier_part(&self, ch: char) -> bool {
77        ch.is_alphabetic() || ch.is_ascii_digit() || ch == '$' || ch == '_'  ||
78        // PostgreSQL implements Unicode characters in identifiers.
79        !ch.is_ascii()
80    }
81
82    fn supports_unicode_string_literal(&self) -> bool {
83        true
84    }
85
86    fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
87        if matches!(kw, Keyword::INTERVAL) {
88            false
89        } else {
90            RESERVED_FOR_IDENTIFIER.contains(&kw)
91        }
92    }
93
94    /// See <https://www.postgresql.org/docs/current/sql-createoperator.html>
95    fn is_custom_operator_part(&self, ch: char) -> bool {
96        matches!(
97            ch,
98            '+' | '-'
99                | '*'
100                | '/'
101                | '<'
102                | '>'
103                | '='
104                | '~'
105                | '!'
106                | '@'
107                | '#'
108                | '%'
109                | '^'
110                | '&'
111                | '|'
112                | '`'
113                | '?'
114        )
115    }
116
117    fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
118        let token = parser.peek_token_ref();
119        debug!("get_next_precedence() {token:?}");
120
121        // we only return some custom value here when the behaviour (not merely the numeric value) differs
122        // from the default implementation
123        match &token.token {
124            Token::Word(w)
125                if w.keyword == Keyword::COLLATE && !parser.in_column_definition_state() =>
126            {
127                Some(Ok(COLLATE_PREC))
128            }
129            Token::LBracket => Some(Ok(BRACKET_PREC)),
130            Token::Arrow
131            | Token::LongArrow
132            | Token::HashArrow
133            | Token::HashLongArrow
134            | Token::AtArrow
135            | Token::ArrowAt
136            | Token::HashMinus
137            | Token::AtQuestion
138            | Token::AtAt
139            | Token::Question
140            | Token::QuestionAnd
141            | Token::QuestionPipe
142            | Token::ExclamationMark
143            | Token::Overlap
144            | Token::CaretAt
145            | Token::StringConcat
146            | Token::Sharp
147            | Token::ShiftRight
148            | Token::ShiftLeft
149            | Token::CustomBinaryOperator(_) => Some(Ok(PG_OTHER_PREC)),
150            // lowest prec to prevent it from turning into a binary op
151            Token::Colon => Some(Ok(self.prec_unknown())),
152            _ => None,
153        }
154    }
155
156    fn supports_filter_during_aggregation(&self) -> bool {
157        true
158    }
159
160    fn supports_group_by_expr(&self) -> bool {
161        true
162    }
163
164    fn supports_alter_user_as_alter_role(&self) -> bool {
165        true
166    }
167
168    fn prec_value(&self, prec: Precedence) -> u8 {
169        match prec {
170            Precedence::Period => PERIOD_PREC,
171            Precedence::DoubleColon => DOUBLE_COLON_PREC,
172            Precedence::AtTz => AT_TZ_PREC,
173            Precedence::MulDivModOp => MUL_DIV_MOD_OP_PREC,
174            Precedence::PlusMinus => PLUS_MINUS_PREC,
175            Precedence::Xor => XOR_PREC,
176            Precedence::Ampersand => PG_OTHER_PREC,
177            Precedence::Caret => CARET_PREC,
178            Precedence::Pipe => PG_OTHER_PREC,
179            Precedence::Colon => PG_OTHER_PREC,
180            Precedence::Between => BETWEEN_LIKE_PREC,
181            Precedence::Eq => EQ_PREC,
182            Precedence::Like => BETWEEN_LIKE_PREC,
183            Precedence::Is => IS_PREC,
184            Precedence::PgOther => PG_OTHER_PREC,
185            Precedence::UnaryNot => NOT_PREC,
186            Precedence::And => AND_PREC,
187            Precedence::Or => OR_PREC,
188        }
189    }
190
191    fn allow_extract_custom(&self) -> bool {
192        true
193    }
194
195    fn allow_extract_single_quotes(&self) -> bool {
196        true
197    }
198
199    fn supports_create_index_with_clause(&self) -> bool {
200        true
201    }
202
203    /// see <https://www.postgresql.org/docs/current/sql-explain.html>
204    fn supports_explain_with_utility_options(&self) -> bool {
205        true
206    }
207
208    /// see <https://www.postgresql.org/docs/current/sql-listen.html>
209    /// see <https://www.postgresql.org/docs/current/sql-unlisten.html>
210    /// see <https://www.postgresql.org/docs/current/sql-notify.html>
211    fn supports_listen_notify(&self) -> bool {
212        true
213    }
214
215    /// see <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-EXCLUDE>
216    fn supports_exclude_constraint(&self) -> bool {
217        true
218    }
219
220    /// see <https://www.postgresql.org/docs/13/functions-math.html>
221    fn supports_factorial_operator(&self) -> bool {
222        true
223    }
224
225    fn supports_bitwise_shift_operators(&self) -> bool {
226        true
227    }
228
229    /// see <https://www.postgresql.org/docs/current/sql-comment.html>
230    fn supports_comment_on(&self) -> bool {
231        true
232    }
233
234    /// See <https://www.postgresql.org/docs/current/sql-load.html>
235    fn supports_load_extension(&self) -> bool {
236        true
237    }
238
239    /// See <https://www.postgresql.org/docs/current/functions-json.html>
240    ///
241    /// Required to support the colon in:
242    /// ```sql
243    /// SELECT json_object('a': 'b')
244    /// ```
245    fn supports_named_fn_args_with_colon_operator(&self) -> bool {
246        true
247    }
248
249    /// See <https://www.postgresql.org/docs/current/functions-json.html>
250    ///
251    /// Required to support the label in:
252    /// ```sql
253    /// SELECT json_object('label': 'value')
254    /// ```
255    fn supports_named_fn_args_with_expr_name(&self) -> bool {
256        true
257    }
258
259    /// Return true if the dialect supports empty projections in SELECT statements
260    ///
261    /// Example
262    /// ```sql
263    /// SELECT from table_name
264    /// ```
265    fn supports_empty_projections(&self) -> bool {
266        true
267    }
268
269    fn supports_nested_comments(&self) -> bool {
270        true
271    }
272
273    fn supports_string_escape_constant(&self) -> bool {
274        true
275    }
276
277    fn supports_numeric_literal_underscores(&self) -> bool {
278        true
279    }
280
281    /// See: <https://www.postgresql.org/docs/current/arrays.html#ARRAYS-DECLARATION>
282    fn supports_array_typedef_with_brackets(&self) -> bool {
283        true
284    }
285
286    fn supports_geometric_types(&self) -> bool {
287        true
288    }
289
290    fn supports_order_by_using_operator(&self) -> bool {
291        true
292    }
293
294    fn supports_set_names(&self) -> bool {
295        true
296    }
297
298    fn supports_alter_column_type_using(&self) -> bool {
299        true
300    }
301
302    /// Postgres supports `NOTNULL` as an alias for `IS NOT NULL`
303    /// See: <https://www.postgresql.org/docs/17/functions-comparison.html>
304    fn supports_notnull_operator(&self) -> bool {
305        true
306    }
307
308    /// [Postgres] supports optional field and precision options for `INTERVAL` data type.
309    ///
310    /// [Postgres]: https://www.postgresql.org/docs/17/datatype-datetime.html
311    fn supports_interval_options(&self) -> bool {
312        true
313    }
314
315    fn supports_insert_table_alias(&self) -> bool {
316        true
317    }
318
319    fn supports_create_table_like_parenthesized(&self) -> bool {
320        true
321    }
322
323    fn supports_select_wildcard_with_alias(&self) -> bool {
324        true
325    }
326
327    fn supports_comma_separated_trim(&self) -> bool {
328        true
329    }
330
331    fn supports_xml_expressions(&self) -> bool {
332        true
333    }
334
335    fn supports_aliased_function_args(&self) -> bool {
336        true
337    }
338
339    /// Postgres supports query optimizer hints via the `pg_hint_plan` extension,
340    /// using the same comment-prefixed-with-`+` syntax as MySQL and Oracle.
341    ///
342    /// See <https://github.com/ossc-db/pg_hint_plan>
343    fn supports_comment_optimizer_hint(&self) -> bool {
344        true
345    }
346}