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, UserDefinedTypeRepresentation};
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 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        // See https://www.postgresql.org/docs/11/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
72        // We don't yet support identifiers beginning with "letters with
73        // diacritical marks"
74        ch.is_alphabetic() || ch == '_'
75    }
76
77    fn is_identifier_part(&self, ch: char) -> bool {
78        ch.is_alphabetic() || ch.is_ascii_digit() || ch == '$' || ch == '_'
79    }
80
81    fn supports_unicode_string_literal(&self) -> bool {
82        true
83    }
84
85    /// See <https://www.postgresql.org/docs/current/sql-createoperator.html>
86    fn is_custom_operator_part(&self, ch: char) -> bool {
87        matches!(
88            ch,
89            '+' | '-'
90                | '*'
91                | '/'
92                | '<'
93                | '>'
94                | '='
95                | '~'
96                | '!'
97                | '@'
98                | '#'
99                | '%'
100                | '^'
101                | '&'
102                | '|'
103                | '`'
104                | '?'
105        )
106    }
107
108    fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
109        let token = parser.peek_token();
110        debug!("get_next_precedence() {:?}", token);
111
112        // we only return some custom value here when the behaviour (not merely the numeric value) differs
113        // from the default implementation
114        match token.token {
115            Token::Word(w) if w.keyword == Keyword::COLLATE => Some(Ok(COLLATE_PREC)),
116            Token::LBracket => Some(Ok(BRACKET_PREC)),
117            Token::Arrow
118            | Token::LongArrow
119            | Token::HashArrow
120            | Token::HashLongArrow
121            | Token::AtArrow
122            | Token::ArrowAt
123            | Token::HashMinus
124            | Token::AtQuestion
125            | Token::AtAt
126            | Token::Question
127            | Token::QuestionAnd
128            | Token::QuestionPipe
129            | Token::ExclamationMark
130            | Token::Overlap
131            | Token::CaretAt
132            | Token::StringConcat
133            | Token::Sharp
134            | Token::ShiftRight
135            | Token::ShiftLeft
136            | Token::CustomBinaryOperator(_) => Some(Ok(PG_OTHER_PREC)),
137            _ => None,
138        }
139    }
140
141    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
142        if parser.parse_keyword(Keyword::CREATE) {
143            parser.prev_token(); // unconsume the CREATE in case we don't end up parsing anything
144            parse_create(parser)
145        } else if parser.parse_keyword(Keyword::LOCK) {
146            parser.prev_token(); // unconsume the LOCK in case we don't end up parsing anything
147            Some(parse_lock_table(parser))
148        } else {
149            None
150        }
151    }
152
153    fn supports_filter_during_aggregation(&self) -> bool {
154        true
155    }
156
157    fn supports_group_by_expr(&self) -> bool {
158        true
159    }
160
161    fn prec_value(&self, prec: Precedence) -> u8 {
162        match prec {
163            Precedence::DoubleColon => DOUBLE_COLON_PREC,
164            Precedence::AtTz => AT_TZ_PREC,
165            Precedence::MulDivModOp => MUL_DIV_MOD_OP_PREC,
166            Precedence::PlusMinus => PLUS_MINUS_PREC,
167            Precedence::Xor => XOR_PREC,
168            Precedence::Ampersand => PG_OTHER_PREC,
169            Precedence::Caret => CARET_PREC,
170            Precedence::Pipe => PG_OTHER_PREC,
171            Precedence::Between => BETWEEN_LIKE_PREC,
172            Precedence::Eq => EQ_PREC,
173            Precedence::Like => BETWEEN_LIKE_PREC,
174            Precedence::Is => IS_PREC,
175            Precedence::PgOther => PG_OTHER_PREC,
176            Precedence::UnaryNot => NOT_PREC,
177            Precedence::And => AND_PREC,
178            Precedence::Or => OR_PREC,
179        }
180    }
181
182    fn allow_extract_custom(&self) -> bool {
183        true
184    }
185
186    fn allow_extract_single_quotes(&self) -> bool {
187        true
188    }
189
190    fn supports_create_index_with_clause(&self) -> bool {
191        true
192    }
193
194    /// see <https://www.postgresql.org/docs/current/sql-explain.html>
195    fn supports_explain_with_utility_options(&self) -> bool {
196        true
197    }
198
199    /// see <https://www.postgresql.org/docs/current/sql-listen.html>
200    /// see <https://www.postgresql.org/docs/current/sql-unlisten.html>
201    /// see <https://www.postgresql.org/docs/current/sql-notify.html>
202    fn supports_listen_notify(&self) -> bool {
203        true
204    }
205
206    /// see <https://www.postgresql.org/docs/13/functions-math.html>
207    fn supports_factorial_operator(&self) -> bool {
208        true
209    }
210
211    /// see <https://www.postgresql.org/docs/current/sql-comment.html>
212    fn supports_comment_on(&self) -> bool {
213        true
214    }
215
216    /// See <https://www.postgresql.org/docs/current/sql-load.html>
217    fn supports_load_extension(&self) -> bool {
218        true
219    }
220
221    /// See <https://www.postgresql.org/docs/current/functions-json.html>
222    ///
223    /// Required to support the colon in:
224    /// ```sql
225    /// SELECT json_object('a': 'b')
226    /// ```
227    fn supports_named_fn_args_with_colon_operator(&self) -> bool {
228        true
229    }
230
231    /// See <https://www.postgresql.org/docs/current/functions-json.html>
232    ///
233    /// Required to support the label in:
234    /// ```sql
235    /// SELECT json_object('label': 'value')
236    /// ```
237    fn supports_named_fn_args_with_expr_name(&self) -> bool {
238        true
239    }
240}
241
242pub fn parse_create(parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
243    let name = parser.maybe_parse(|parser| -> Result<ObjectName, ParserError> {
244        parser.expect_keyword(Keyword::CREATE)?;
245        parser.expect_keyword(Keyword::TYPE)?;
246        let name = parser.parse_object_name(false)?;
247        parser.expect_keyword(Keyword::AS)?;
248        parser.expect_keyword(Keyword::ENUM)?;
249        Ok(name)
250    });
251
252    match name {
253        Ok(name) => name.map(|name| parse_create_type_as_enum(parser, name)),
254        Err(e) => Some(Err(e)),
255    }
256}
257
258// https://www.postgresql.org/docs/current/sql-createtype.html
259pub fn parse_create_type_as_enum(
260    parser: &mut Parser,
261    name: ObjectName,
262) -> Result<Statement, ParserError> {
263    if !parser.consume_token(&Token::LParen) {
264        return parser.expected("'(' after CREATE TYPE AS ENUM", parser.peek_token());
265    }
266
267    let labels = parser.parse_comma_separated0(|p| p.parse_identifier(false), Token::RParen)?;
268    parser.expect_token(&Token::RParen)?;
269
270    Ok(Statement::CreateType {
271        name,
272        representation: UserDefinedTypeRepresentation::Enum { labels },
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}