sqltk_parser/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::ast::{LockTableType, LockTables, ObjectName, Statement};
32use crate::dialect::{Dialect, Precedence};
33use crate::keywords::Keyword;
34use crate::parser::{Parser, ParserError};
35use crate::tokenizer::Token;
36
37#[cfg(not(feature = "std"))]
38use alloc::vec::Vec;
39
40/// A [`Dialect`] for [PostgreSQL](https://www.postgresql.org/)
41#[derive(Debug)]
42pub struct PostgreSqlDialect {}
43
44const PERIOD_PREC: u8 = 200;
45const DOUBLE_COLON_PREC: u8 = 140;
46const BRACKET_PREC: u8 = 130;
47const COLLATE_PREC: u8 = 120;
48const AT_TZ_PREC: u8 = 110;
49const CARET_PREC: u8 = 100;
50const MUL_DIV_MOD_OP_PREC: u8 = 90;
51const PLUS_MINUS_PREC: u8 = 80;
52// there's no XOR operator in PostgreSQL, but support it here to avoid breaking tests
53const XOR_PREC: u8 = 75;
54const PG_OTHER_PREC: u8 = 70;
55const BETWEEN_LIKE_PREC: u8 = 60;
56const EQ_PREC: u8 = 50;
57const IS_PREC: u8 = 40;
58const NOT_PREC: u8 = 30;
59const AND_PREC: u8 = 20;
60const OR_PREC: u8 = 10;
61
62impl Dialect for PostgreSqlDialect {
63    fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
64        Some('"')
65    }
66
67    fn is_delimited_identifier_start(&self, ch: char) -> bool {
68        ch == '"' // Postgres does not support backticks to quote identifiers
69    }
70
71    fn is_identifier_start(&self, ch: char) -> bool {
72        // See https://www.postgresql.org/docs/11/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
73        // We don't yet support identifiers beginning with "letters with
74        // diacritical marks"
75        ch.is_alphabetic() || ch == '_'
76    }
77
78    fn is_identifier_part(&self, ch: char) -> bool {
79        ch.is_alphabetic() || ch.is_ascii_digit() || ch == '$' || ch == '_'
80    }
81
82    fn supports_unicode_string_literal(&self) -> bool {
83        true
84    }
85
86    /// See <https://www.postgresql.org/docs/current/sql-createoperator.html>
87    fn is_custom_operator_part(&self, ch: char) -> bool {
88        matches!(
89            ch,
90            '+' | '-'
91                | '*'
92                | '/'
93                | '<'
94                | '>'
95                | '='
96                | '~'
97                | '!'
98                | '@'
99                | '#'
100                | '%'
101                | '^'
102                | '&'
103                | '|'
104                | '`'
105                | '?'
106        )
107    }
108
109    fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
110        let token = parser.peek_token();
111        debug!("get_next_precedence() {:?}", token);
112
113        // we only return some custom value here when the behaviour (not merely the numeric value) differs
114        // from the default implementation
115        match token.token {
116            Token::Word(w) if w.keyword == Keyword::COLLATE => Some(Ok(COLLATE_PREC)),
117            Token::LBracket => Some(Ok(BRACKET_PREC)),
118            Token::Arrow
119            | Token::LongArrow
120            | Token::HashArrow
121            | Token::HashLongArrow
122            | Token::AtArrow
123            | Token::ArrowAt
124            | Token::HashMinus
125            | Token::AtQuestion
126            | Token::AtAt
127            | Token::Question
128            | Token::QuestionAnd
129            | Token::QuestionPipe
130            | Token::ExclamationMark
131            | Token::Overlap
132            | Token::CaretAt
133            | Token::StringConcat
134            | Token::Sharp
135            | Token::ShiftRight
136            | Token::ShiftLeft
137            | Token::CustomBinaryOperator(_) => Some(Ok(PG_OTHER_PREC)),
138            _ => None,
139        }
140    }
141
142    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
143        if parser.parse_keyword(Keyword::LOCK) {
144            parser.prev_token(); // unconsume the LOCK in case we don't end up parsing anything
145            Some(parse_lock_table(parser))
146        } else {
147            None
148        }
149    }
150
151    fn supports_filter_during_aggregation(&self) -> bool {
152        true
153    }
154
155    fn supports_group_by_expr(&self) -> bool {
156        true
157    }
158
159    fn prec_value(&self, prec: Precedence) -> u8 {
160        match prec {
161            Precedence::Period => PERIOD_PREC,
162            Precedence::DoubleColon => DOUBLE_COLON_PREC,
163            Precedence::AtTz => AT_TZ_PREC,
164            Precedence::MulDivModOp => MUL_DIV_MOD_OP_PREC,
165            Precedence::PlusMinus => PLUS_MINUS_PREC,
166            Precedence::Xor => XOR_PREC,
167            Precedence::Ampersand => PG_OTHER_PREC,
168            Precedence::Caret => CARET_PREC,
169            Precedence::Pipe => PG_OTHER_PREC,
170            Precedence::Between => BETWEEN_LIKE_PREC,
171            Precedence::Eq => EQ_PREC,
172            Precedence::Like => BETWEEN_LIKE_PREC,
173            Precedence::Is => IS_PREC,
174            Precedence::PgOther => PG_OTHER_PREC,
175            Precedence::UnaryNot => NOT_PREC,
176            Precedence::And => AND_PREC,
177            Precedence::Or => OR_PREC,
178        }
179    }
180
181    fn allow_extract_custom(&self) -> bool {
182        true
183    }
184
185    fn allow_extract_single_quotes(&self) -> bool {
186        true
187    }
188
189    fn supports_create_index_with_clause(&self) -> bool {
190        true
191    }
192
193    /// see <https://www.postgresql.org/docs/current/sql-explain.html>
194    fn supports_explain_with_utility_options(&self) -> bool {
195        true
196    }
197
198    /// see <https://www.postgresql.org/docs/current/sql-listen.html>
199    /// see <https://www.postgresql.org/docs/current/sql-unlisten.html>
200    /// see <https://www.postgresql.org/docs/current/sql-notify.html>
201    fn supports_listen_notify(&self) -> bool {
202        true
203    }
204
205    /// see <https://www.postgresql.org/docs/13/functions-math.html>
206    fn supports_factorial_operator(&self) -> bool {
207        true
208    }
209
210    /// see <https://www.postgresql.org/docs/current/sql-comment.html>
211    fn supports_comment_on(&self) -> bool {
212        true
213    }
214
215    /// See <https://www.postgresql.org/docs/current/sql-load.html>
216    fn supports_load_extension(&self) -> bool {
217        true
218    }
219
220    /// See <https://www.postgresql.org/docs/current/functions-json.html>
221    ///
222    /// Required to support the colon in:
223    /// ```sql
224    /// SELECT json_object('a': 'b')
225    /// ```
226    fn supports_named_fn_args_with_colon_operator(&self) -> bool {
227        true
228    }
229
230    /// See <https://www.postgresql.org/docs/current/functions-json.html>
231    ///
232    /// Required to support the label in:
233    /// ```sql
234    /// SELECT json_object('label': 'value')
235    /// ```
236    fn supports_named_fn_args_with_expr_name(&self) -> bool {
237        true
238    }
239
240    /// Return true if the dialect supports empty projections in SELECT statements
241    ///
242    /// Example
243    /// ```sql
244    /// SELECT from table_name
245    /// ```
246    fn supports_empty_projections(&self) -> bool {
247        true
248    }
249
250    fn supports_nested_comments(&self) -> bool {
251        true
252    }
253
254    fn supports_string_escape_constant(&self) -> bool {
255        true
256    }
257
258    fn supports_numeric_literal_underscores(&self) -> bool {
259        true
260    }
261
262    /// See: <https://www.postgresql.org/docs/current/arrays.html#ARRAYS-DECLARATION>
263    fn supports_array_typedef_with_brackets(&self) -> bool {
264        true
265    }
266
267    fn supports_geometric_types(&self) -> bool {
268        true
269    }
270
271    fn supports_set_names(&self) -> bool {
272        true
273    }
274}
275
276pub fn parse_lock_table(parser: &mut Parser) -> Result<Statement, ParserError> {
277    parser.expect_keyword(Keyword::LOCK)?;
278    let has_table_keyword = parser.parse_keyword(Keyword::TABLE);
279    let only = parser.parse_keyword(Keyword::ONLY);
280    let tables: Vec<ObjectName> =
281        parser.parse_comma_separated(|parser| parser.parse_object_name(false))?;
282    let lock_mode = parse_lock_mode(parser)?;
283    let no_wait = parser.parse_keyword(Keyword::NOWAIT);
284
285    Ok(Statement::LockTables(LockTables::Postgres {
286        tables,
287        lock_mode,
288        has_table_keyword,
289        only,
290        no_wait,
291    }))
292}
293
294pub fn parse_lock_mode(parser: &mut Parser) -> Result<Option<LockTableType>, ParserError> {
295    if !parser.parse_keyword(Keyword::IN) {
296        return Ok(None);
297    }
298
299    let lock_mode = if parser.parse_keywords(&[Keyword::ACCESS, Keyword::SHARE]) {
300        LockTableType::AccessShare
301    } else if parser.parse_keywords(&[Keyword::ACCESS, Keyword::EXCLUSIVE]) {
302        LockTableType::AccessExclusive
303    } else if parser.parse_keywords(&[Keyword::EXCLUSIVE]) {
304        LockTableType::Exclusive
305    } else if parser.parse_keywords(&[Keyword::ROW, Keyword::EXCLUSIVE]) {
306        LockTableType::RowExclusive
307    } else if parser.parse_keywords(&[Keyword::ROW, Keyword::SHARE]) {
308        LockTableType::RowShare
309    } else if parser.parse_keywords(&[Keyword::SHARE, Keyword::ROW, Keyword::EXCLUSIVE]) {
310        LockTableType::ShareRowExclusive
311    } else if parser.parse_keywords(&[Keyword::SHARE, Keyword::UPDATE, Keyword::EXCLUSIVE]) {
312        LockTableType::ShareUpdateExclusive
313    } else if parser.parse_keywords(&[Keyword::SHARE]) {
314        LockTableType::Share
315    } else {
316        return Err(ParserError::ParserError("Expected: ACCESS EXCLUSIVE | ACCESS SHARE | EXCLUSIVE | ROW EXCLUSIVE | ROW SHARE | SHARE | SHARE ROW EXCLUSIVE | SHARE ROW EXCLUSIVE".into()));
317    };
318
319    parser.expect_keyword(Keyword::MODE)?;
320
321    Ok(Some(lock_mode))
322}