1use crate::{
13 Expression, Identifier, QualifiedName, Span, Spanned,
14 alter_table::{IndexCol, IndexColExpr, parse_operator_class},
15 create_option::CreateOption,
16 expression::{PRIORITY_MAX, parse_expression_unreserved},
17 keywords::Keyword,
18 lexer::Token,
19 parser::{ParseError, Parser},
20 qualified_name::parse_qualified_name_unreserved,
21};
22
23#[derive(Clone, Debug)]
25pub struct WithOption<'a> {
26 pub name: Identifier<'a>,
27 pub eq_span: Span,
28 pub value: Expression<'a>,
29}
30
31impl<'a> Spanned for WithOption<'a> {
32 fn span(&self) -> Span {
33 self.name.join_span(&self.value)
34 }
35}
36use alloc::vec::Vec;
37
38#[derive(Clone, Debug)]
39pub enum UsingIndexMethod {
40 Gist(Span),
41 Bloom(Span),
42 Brin(Span),
43 Hnsw(Span),
44 Gin(Span),
45 BTree(Span),
46 Hash(Span),
47 RTree(Span),
48}
49
50impl Spanned for UsingIndexMethod {
51 fn span(&self) -> Span {
52 match self {
53 UsingIndexMethod::Gist(s) => s.clone(),
54 UsingIndexMethod::Bloom(s) => s.clone(),
55 UsingIndexMethod::Brin(s) => s.clone(),
56 UsingIndexMethod::Hnsw(s) => s.clone(),
57 UsingIndexMethod::BTree(s) => s.clone(),
58 UsingIndexMethod::Hash(s) => s.clone(),
59 UsingIndexMethod::RTree(s) => s.clone(),
60 UsingIndexMethod::Gin(s) => s.clone(),
61 }
62 }
63}
64
65pub(crate) fn parse_using_index_method<'a>(
66 parser: &mut Parser<'a, '_>,
67 using_span: Span,
68) -> Result<UsingIndexMethod, ParseError> {
69 match &parser.token {
70 Token::Ident(_, Keyword::GIST) => {
71 let gist_span = parser.consume_keyword(Keyword::GIST)?;
72 Ok(UsingIndexMethod::Gist(using_span.join_span(&gist_span)))
73 }
74 Token::Ident(_, Keyword::BLOOM) => {
75 let bloom_span = parser.consume_keyword(Keyword::BLOOM)?;
76 Ok(UsingIndexMethod::Bloom(using_span.join_span(&bloom_span)))
77 }
78 Token::Ident(_, Keyword::BRIN) => {
79 let brin_span = parser.consume_keyword(Keyword::BRIN)?;
80 Ok(UsingIndexMethod::Brin(using_span.join_span(&brin_span)))
81 }
82 Token::Ident(_, Keyword::HNSW) => {
83 let hnsw_span = parser.consume_keyword(Keyword::HNSW)?;
84 Ok(UsingIndexMethod::Hnsw(using_span.join_span(&hnsw_span)))
85 }
86 Token::Ident(_, Keyword::GIN) => {
87 let gin_span = parser.consume_keyword(Keyword::GIN)?;
88 Ok(UsingIndexMethod::Gin(using_span.join_span(&gin_span)))
89 }
90 Token::Ident(_, Keyword::BTREE) => {
91 let btree_span = parser.consume_keyword(Keyword::BTREE)?;
92 Ok(UsingIndexMethod::BTree(using_span.join_span(&btree_span)))
93 }
94 Token::Ident(_, Keyword::HASH) => {
95 let hash_span = parser.consume_keyword(Keyword::HASH)?;
96 Ok(UsingIndexMethod::Hash(using_span.join_span(&hash_span)))
97 }
98 Token::Ident(_, Keyword::RTREE) => {
99 let rtree_span = parser.consume_keyword(Keyword::RTREE)?;
100 Ok(UsingIndexMethod::RTree(using_span.join_span(&rtree_span)))
101 }
102 _ => Err(parser
103 .err_here("Expected GIST, BLOOM, BRIN, HNSW, BTREE, HASH, or RTREE after USING")?),
104 }
105}
106
107#[derive(Clone, Debug)]
108pub enum CreateIndexOption<'a> {
109 UsingIndex(UsingIndexMethod),
110 Algorithm(Span, Identifier<'a>),
111 AlgorithmDefault {
112 algorithm_span: Span,
113 default_span: Span,
114 },
115 Lock(Span, Identifier<'a>),
116}
117
118impl<'a> Spanned for CreateIndexOption<'a> {
119 fn span(&self) -> Span {
120 match self {
121 CreateIndexOption::UsingIndex(method) => method.span(),
122 CreateIndexOption::Algorithm(s, i) => s.join_span(i),
123 CreateIndexOption::AlgorithmDefault {
124 algorithm_span,
125 default_span,
126 } => algorithm_span.join_span(default_span),
127 CreateIndexOption::Lock(s, i) => s.join_span(i),
128 }
129 }
130}
131
132#[derive(Clone, Debug)]
133pub struct IncludeClause<'a> {
134 pub include_span: Span,
135 pub l_paren_span: Span,
136 pub columns: Vec<Identifier<'a>>,
137 pub r_paren_span: Span,
138}
139
140impl<'a> Spanned for IncludeClause<'a> {
141 fn span(&self) -> Span {
142 self.include_span
143 .join_span(&self.l_paren_span)
144 .join_span(&self.columns)
145 .join_span(&self.r_paren_span)
146 }
147}
148
149#[derive(Clone, Debug)]
150pub struct CreateIndex<'a> {
151 pub create_span: Span,
152 pub create_options: Vec<CreateOption<'a>>,
153 pub index_span: Span,
154 pub index_name: Option<Identifier<'a>>,
155 pub if_not_exists: Option<Span>,
156 pub on_span: Span,
157 pub table_name: QualifiedName<'a>,
158 pub index_options: Vec<CreateIndexOption<'a>>,
159 pub l_paren_span: Span,
160 pub column_names: Vec<IndexCol<'a>>,
161 pub r_paren_span: Span,
162 pub include_clause: Option<IncludeClause<'a>>,
163 pub with_options: Option<(Span, Vec<WithOption<'a>>)>,
164 pub where_: Option<(Span, Expression<'a>)>,
165 pub nulls_distinct: Option<(Span, Option<Span>)>,
166}
167
168impl<'a> Spanned for CreateIndex<'a> {
169 fn span(&self) -> Span {
170 self.create_span
171 .join_span(&self.create_options)
172 .join_span(&self.index_span)
173 .join_span(&self.index_name)
174 .join_span(&self.on_span)
175 .join_span(&self.table_name)
176 .join_span(&self.index_options)
177 .join_span(&self.l_paren_span)
178 .join_span(&self.column_names)
179 .join_span(&self.r_paren_span)
180 .join_span(&self.include_clause)
181 .join_span(&self.with_options.as_ref().map(|(s, c)| s.join_span(c)))
182 .join_span(&self.where_)
183 .join_span(&self.nulls_distinct)
184 }
185}
186
187pub(crate) fn parse_create_index<'a>(
188 parser: &mut Parser<'a, '_>,
189 create_span: Span,
190 mut create_options: Vec<CreateOption<'a>>,
191) -> Result<CreateIndex<'a>, ParseError> {
192 let index_span = parser.consume_keyword(Keyword::INDEX)?;
193
194 if let Some(concurrently_span) = parser.skip_keyword(Keyword::CONCURRENTLY) {
196 parser.postgres_only(&concurrently_span);
197 create_options.push(CreateOption::Concurrently(concurrently_span));
198 }
199
200 let if_not_exists = if let Some(s) = parser.skip_keyword(Keyword::IF) {
201 Some(s.join_span(&parser.consume_keywords(&[Keyword::NOT, Keyword::EXISTS])?))
202 } else {
203 None
204 };
205
206 let index_name = if let Token::Ident(_, Keyword::ON) = &parser.token {
208 None
210 } else {
211 Some(parser.consume_plain_identifier_unreserved()?)
213 };
214
215 if index_name.is_none() && parser.options.dialect.is_maria() {
217 parser.err("Index name required", &index_span);
218 }
219
220 let on_span = parser.consume_keyword(Keyword::ON)?;
221 let table_name = parse_qualified_name_unreserved(parser)?;
222
223 let mut index_options = Vec::new();
225 if let Some(using_span) = parser.skip_keyword(Keyword::USING) {
226 let using_index_method = parse_using_index_method(parser, using_span)?;
227 index_options.push(CreateIndexOption::UsingIndex(using_index_method));
228 }
229
230 let l_paren_span = parser.consume_token(Token::LParen)?;
231 let mut column_names = Vec::new();
232 loop {
233 let expr = if parser.token == Token::LParen {
235 parser.consume_token(Token::LParen)?;
237 let expression = parse_expression_unreserved(parser, PRIORITY_MAX)?;
238 parser.consume_token(Token::RParen)?;
239 IndexColExpr::Expression(expression)
240 } else if matches!(&parser.token, Token::Ident(_, _))
241 && matches!(parser.peek(), Token::LParen)
242 && parser.options.dialect.is_postgresql()
243 {
244 IndexColExpr::Expression(parse_expression_unreserved(parser, PRIORITY_MAX)?)
246 } else {
247 let name = parser.consume_plain_identifier_unreserved()?;
249 IndexColExpr::Column(name)
250 };
251
252 let size = if parser.skip_token(Token::LParen).is_some() {
253 let size = parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
254 parser.consume_int()
255 })?;
256 parser.consume_token(Token::RParen)?;
257 Some(size)
258 } else {
259 None
260 };
261
262 let opclass = parse_operator_class(parser)?;
264
265 let asc = parser.skip_keyword(Keyword::ASC);
267 let desc = if asc.is_none() {
268 parser.skip_keyword(Keyword::DESC)
269 } else {
270 None
271 };
272
273 column_names.push(IndexCol {
274 expr,
275 size,
276 opclass,
277 asc,
278 desc,
279 });
280
281 if parser.skip_token(Token::Comma).is_none() {
282 break;
283 }
284 }
285
286 let r_paren_span = parser.consume_token(Token::RParen)?;
287
288 let include_clause = if let Some(include_span) = parser.skip_keyword(Keyword::INCLUDE) {
290 let l_paren = parser.consume_token(Token::LParen)?;
291 let mut include_cols = Vec::new();
292 loop {
293 include_cols.push(parser.consume_plain_identifier_unreserved()?);
294 if parser.skip_token(Token::Comma).is_none() {
295 break;
296 }
297 }
298 let r_paren = parser.consume_token(Token::RParen)?;
299 parser.postgres_only(&include_span);
300 Some(IncludeClause {
301 include_span,
302 l_paren_span: l_paren,
303 columns: include_cols,
304 r_paren_span: r_paren,
305 })
306 } else {
307 None
308 };
309
310 let with_options = if let Some(with_span) = parser.skip_keyword(Keyword::WITH) {
312 parser.postgres_only(&with_span);
313 parser.consume_token(Token::LParen)?;
314 let mut opts = Vec::new();
315 parser.recovered("')'", &|t| t == &Token::RParen, |parser| {
316 loop {
317 let name = parser.consume_plain_identifier_unreserved()?;
318 let eq_span = parser.consume_token(Token::Eq)?;
319 let value = parse_expression_unreserved(parser, PRIORITY_MAX)?;
320 opts.push(WithOption {
321 name,
322 eq_span,
323 value,
324 });
325 if parser.skip_token(Token::Comma).is_none() {
326 break;
327 }
328 }
329 Ok(())
330 })?;
331 parser.consume_token(Token::RParen)?;
332 Some((with_span, opts))
333 } else {
334 None
335 };
336
337 loop {
339 match &parser.token {
340 Token::Ident(_, Keyword::USING) => {
341 let using_span = parser.consume_keyword(Keyword::USING)?;
342 let using_index_method = parse_using_index_method(parser, using_span)?;
343 index_options.push(CreateIndexOption::UsingIndex(using_index_method));
344 }
345 Token::Ident(_, Keyword::ALGORITHM) => {
346 let algorithm_span = parser.consume_keyword(Keyword::ALGORITHM)?;
347 parser.skip_token(Token::Eq); if matches!(&parser.token, Token::Ident(_, Keyword::DEFAULT)) {
349 let default_span = parser.consume_keyword(Keyword::DEFAULT)?;
350 index_options.push(CreateIndexOption::AlgorithmDefault {
351 algorithm_span,
352 default_span,
353 });
354 } else {
355 let algorithm_value = parser.consume_plain_identifier_unreserved()?;
356 index_options.push(CreateIndexOption::Algorithm(
357 algorithm_span,
358 algorithm_value,
359 ));
360 }
361 }
362 Token::Ident(_, Keyword::LOCK) => {
363 let lock_span = parser.consume_keyword(Keyword::LOCK)?;
364 parser.skip_token(Token::Eq); let lock_value = parser.consume_plain_identifier_unreserved()?;
366 index_options.push(CreateIndexOption::Lock(lock_span, lock_value));
367 }
368 _ => break,
369 }
370 }
371
372 let mut where_ = None;
373 if let Some(where_span) = parser.skip_keyword(Keyword::WHERE) {
374 let where_expr = parse_expression_unreserved(parser, PRIORITY_MAX)?;
375 if parser.options.dialect.is_maria() {
376 parser.err(
377 "Partial indexes not supported",
378 &where_span.join_span(&where_expr),
379 );
380 }
381 where_ = Some((where_span, where_expr));
382 }
383
384 let nulls_distinct = if let Some(nulls_span) = parser.skip_keyword(Keyword::NULLS) {
386 let not_span = parser.skip_keyword(Keyword::NOT);
387 let distinct_span = parser.consume_keyword(Keyword::DISTINCT)?;
388 parser.postgres_only(&nulls_span.join_span(&distinct_span));
389 Some((nulls_span, not_span))
390 } else {
391 None
392 };
393
394 Ok(CreateIndex {
395 create_span,
396 create_options,
397 index_span,
398 index_name,
399 if_not_exists,
400 on_span,
401 table_name,
402 index_options,
403 l_paren_span,
404 column_names,
405 r_paren_span,
406 include_clause,
407 with_options,
408 where_,
409 nulls_distinct,
410 })
411}