sqltk_parser/dialect/
postgresql.rs1use 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#[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;
48const 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 == '"' }
66
67 fn is_identifier_start(&self, ch: char) -> bool {
68 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 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 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(); 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 fn supports_explain_with_utility_options(&self) -> bool {
192 true
193 }
194
195 fn supports_listen(&self) -> bool {
197 true
198 }
199
200 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
258pub 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}