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::{CommentObject, ObjectName, Statement, UserDefinedTypeRepresentation};
32use crate::dialect::{Dialect, Precedence};
33use crate::keywords::Keyword;
34use crate::parser::{Parser, ParserError};
35use crate::tokenizer::Token;
36
37/// A [`Dialect`] for [PostgreSQL](https://www.postgresql.org/)
38#[derive(Debug)]
39pub struct PostgreSqlDialect {}
40
41const DOUBLE_COLON_PREC: u8 = 140;
42const BRACKET_PREC: u8 = 130;
43const COLLATE_PREC: u8 = 120;
44const AT_TZ_PREC: u8 = 110;
45const CARET_PREC: u8 = 100;
46const MUL_DIV_MOD_OP_PREC: u8 = 90;
47const PLUS_MINUS_PREC: u8 = 80;
48// there's no XOR operator in PostgreSQL, but support it here to avoid breaking tests
49const XOR_PREC: u8 = 75;
50const PG_OTHER_PREC: u8 = 70;
51const BETWEEN_LIKE_PREC: u8 = 60;
52const EQ_PREC: u8 = 50;
53const IS_PREC: u8 = 40;
54const NOT_PREC: u8 = 30;
55const AND_PREC: u8 = 20;
56const OR_PREC: u8 = 10;
57
58impl Dialect for PostgreSqlDialect {
59    fn identifier_quote_style(&self, _identifier: &str) -> Option<char> {
60        Some('"')
61    }
62
63    fn is_delimited_identifier_start(&self, ch: char) -> bool {
64        ch == '"' // Postgres does not support backticks to quote identifiers
65    }
66
67    fn is_identifier_start(&self, ch: char) -> bool {
68        // See https://www.postgresql.org/docs/11/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
69        // We don't yet support identifiers beginning with "letters with
70        // diacritical marks"
71        ch.is_alphabetic() || ch == '_'
72    }
73
74    fn is_identifier_part(&self, ch: char) -> bool {
75        ch.is_alphabetic() || ch.is_ascii_digit() || ch == '$' || ch == '_'
76    }
77
78    fn supports_unicode_string_literal(&self) -> bool {
79        true
80    }
81
82    /// See <https://www.postgresql.org/docs/current/sql-createoperator.html>
83    fn is_custom_operator_part(&self, ch: char) -> bool {
84        matches!(
85            ch,
86            '+' | '-'
87                | '*'
88                | '/'
89                | '<'
90                | '>'
91                | '='
92                | '~'
93                | '!'
94                | '@'
95                | '#'
96                | '%'
97                | '^'
98                | '&'
99                | '|'
100                | '`'
101                | '?'
102        )
103    }
104
105    fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
106        let token = parser.peek_token();
107        debug!("get_next_precedence() {:?}", token);
108
109        // we only return some custom value here when the behaviour (not merely the numeric value) differs
110        // from the default implementation
111        match token.token {
112            Token::Word(w) if w.keyword == Keyword::COLLATE => Some(Ok(COLLATE_PREC)),
113            Token::LBracket => Some(Ok(BRACKET_PREC)),
114            Token::Arrow
115            | Token::LongArrow
116            | Token::HashArrow
117            | Token::HashLongArrow
118            | Token::AtArrow
119            | Token::ArrowAt
120            | Token::HashMinus
121            | Token::AtQuestion
122            | Token::AtAt
123            | Token::Question
124            | Token::QuestionAnd
125            | Token::QuestionPipe
126            | Token::ExclamationMark
127            | Token::Overlap
128            | Token::CaretAt
129            | Token::StringConcat
130            | Token::Sharp
131            | Token::ShiftRight
132            | Token::ShiftLeft
133            | Token::CustomBinaryOperator(_) => Some(Ok(PG_OTHER_PREC)),
134            _ => None,
135        }
136    }
137
138    fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
139        if parser.parse_keyword(Keyword::COMMENT) {
140            Some(parse_comment(parser))
141        } else if parser.parse_keyword(Keyword::CREATE) {
142            parser.prev_token(); // unconsume the CREATE in case we don't end up parsing anything
143            parse_create(parser)
144        } else {
145            None
146        }
147    }
148
149    fn supports_filter_during_aggregation(&self) -> bool {
150        true
151    }
152
153    fn supports_group_by_expr(&self) -> bool {
154        true
155    }
156
157    fn prec_value(&self, prec: Precedence) -> u8 {
158        match prec {
159            Precedence::DoubleColon => DOUBLE_COLON_PREC,
160            Precedence::AtTz => AT_TZ_PREC,
161            Precedence::MulDivModOp => MUL_DIV_MOD_OP_PREC,
162            Precedence::PlusMinus => PLUS_MINUS_PREC,
163            Precedence::Xor => XOR_PREC,
164            Precedence::Ampersand => PG_OTHER_PREC,
165            Precedence::Caret => CARET_PREC,
166            Precedence::Pipe => PG_OTHER_PREC,
167            Precedence::Between => BETWEEN_LIKE_PREC,
168            Precedence::Eq => EQ_PREC,
169            Precedence::Like => BETWEEN_LIKE_PREC,
170            Precedence::Is => IS_PREC,
171            Precedence::PgOther => PG_OTHER_PREC,
172            Precedence::UnaryNot => NOT_PREC,
173            Precedence::And => AND_PREC,
174            Precedence::Or => OR_PREC,
175        }
176    }
177
178    fn allow_extract_custom(&self) -> bool {
179        true
180    }
181
182    fn allow_extract_single_quotes(&self) -> bool {
183        true
184    }
185
186    fn supports_create_index_with_clause(&self) -> bool {
187        true
188    }
189
190    /// see <https://www.postgresql.org/docs/current/sql-explain.html>
191    fn supports_explain_with_utility_options(&self) -> bool {
192        true
193    }
194
195    /// see <https://www.postgresql.org/docs/current/sql-listen.html>
196    fn supports_listen(&self) -> bool {
197        true
198    }
199
200    /// see <https://www.postgresql.org/docs/current/sql-notify.html>
201    fn supports_notify(&self) -> bool {
202        true
203    }
204}
205
206pub fn parse_comment(parser: &mut Parser) -> Result<Statement, ParserError> {
207    let if_exists = parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
208
209    parser.expect_keyword(Keyword::ON)?;
210    let token = parser.next_token();
211
212    let (object_type, object_name) = match token.token {
213        Token::Word(w) if w.keyword == Keyword::COLUMN => {
214            let object_name = parser.parse_object_name(false)?;
215            (CommentObject::Column, object_name)
216        }
217        Token::Word(w) if w.keyword == Keyword::TABLE => {
218            let object_name = parser.parse_object_name(false)?;
219            (CommentObject::Table, object_name)
220        }
221        Token::Word(w) if w.keyword == Keyword::EXTENSION => {
222            let object_name = parser.parse_object_name(false)?;
223            (CommentObject::Extension, object_name)
224        }
225        _ => parser.expected("comment object_type", token)?,
226    };
227
228    parser.expect_keyword(Keyword::IS)?;
229    let comment = if parser.parse_keyword(Keyword::NULL) {
230        None
231    } else {
232        Some(parser.parse_literal_string()?)
233    };
234    Ok(Statement::Comment {
235        object_type,
236        object_name,
237        comment,
238        if_exists,
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}