Skip to content

Commit 6ec5223

Browse files
Extend support for INDEX parsing (#1707)
Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent d5dbe86 commit 6ec5223

File tree

8 files changed

+405
-61
lines changed

8 files changed

+405
-61
lines changed

Diff for: src/ast/ddl.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -1174,20 +1174,33 @@ impl fmt::Display for KeyOrIndexDisplay {
11741174
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
11751175
/// [2]: https://dev.mysql.com/doc/refman/8.0/en/create-index.html
11761176
/// [3]: https://www.postgresql.org/docs/14/sql-createindex.html
1177-
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1177+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
11781178
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11791179
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
11801180
pub enum IndexType {
11811181
BTree,
11821182
Hash,
1183-
// TODO add Postgresql's possible indexes
1183+
GIN,
1184+
GiST,
1185+
SPGiST,
1186+
BRIN,
1187+
Bloom,
1188+
/// Users may define their own index types, which would
1189+
/// not be covered by the above variants.
1190+
Custom(Ident),
11841191
}
11851192

11861193
impl fmt::Display for IndexType {
11871194
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11881195
match self {
11891196
Self::BTree => write!(f, "BTREE"),
11901197
Self::Hash => write!(f, "HASH"),
1198+
Self::GIN => write!(f, "GIN"),
1199+
Self::GiST => write!(f, "GIST"),
1200+
Self::SPGiST => write!(f, "SPGIST"),
1201+
Self::BRIN => write!(f, "BRIN"),
1202+
Self::Bloom => write!(f, "BLOOM"),
1203+
Self::Custom(name) => write!(f, "{}", name),
11911204
}
11921205
}
11931206
}

Diff for: src/ast/dml.rs

+25-6
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,31 @@ pub use super::ddl::{ColumnDef, TableConstraint};
3434
use super::{
3535
display_comma_separated, display_separated, query::InputFormatClause, Assignment, ClusteredBy,
3636
CommentDef, Expr, FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
37-
HiveRowFormat, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert,
38-
OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting, SqlOption,
39-
SqliteOnConflict, StorageSerializationPolicy, TableEngine, TableObject, TableWithJoins, Tag,
40-
WrappedCollection,
37+
HiveRowFormat, Ident, IndexType, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit,
38+
OnInsert, OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting,
39+
SqlOption, SqliteOnConflict, StorageSerializationPolicy, TableEngine, TableObject,
40+
TableWithJoins, Tag, WrappedCollection,
4141
};
4242

43+
/// Index column type.
44+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
45+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
47+
pub struct IndexColumn {
48+
pub column: OrderByExpr,
49+
pub operator_class: Option<Ident>,
50+
}
51+
52+
impl Display for IndexColumn {
53+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54+
write!(f, "{}", self.column)?;
55+
if let Some(operator_class) = &self.operator_class {
56+
write!(f, " {}", operator_class)?;
57+
}
58+
Ok(())
59+
}
60+
}
61+
4362
/// CREATE INDEX statement.
4463
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4564
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -49,8 +68,8 @@ pub struct CreateIndex {
4968
pub name: Option<ObjectName>,
5069
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
5170
pub table_name: ObjectName,
52-
pub using: Option<Ident>,
53-
pub columns: Vec<OrderByExpr>,
71+
pub using: Option<IndexType>,
72+
pub columns: Vec<IndexColumn>,
5473
pub unique: bool,
5574
pub concurrently: bool,
5675
pub if_not_exists: bool,

Diff for: src/ast/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub use self::ddl::{
5858
ReferentialAction, TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef,
5959
UserDefinedTypeRepresentation, ViewColumnDef,
6060
};
61-
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
61+
pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
6262
pub use self::operator::{BinaryOperator, UnaryOperator};
6363
pub use self::query::{
6464
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
@@ -91,6 +91,7 @@ pub use self::value::{
9191

9292
use crate::ast::helpers::key_value_options::KeyValueOptions;
9393
use crate::ast::helpers::stmt_data_loading::{StageLoadSelectItem, StageParamsObject};
94+
9495
#[cfg(feature = "visitor")]
9596
pub use visitor::*;
9697

Diff for: src/ast/spans.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ impl Spanned for CreateIndex {
704704
let CreateIndex {
705705
name,
706706
table_name,
707-
using,
707+
using: _,
708708
columns,
709709
unique: _, // bool
710710
concurrently: _, // bool
@@ -719,8 +719,7 @@ impl Spanned for CreateIndex {
719719
name.iter()
720720
.map(|i| i.span())
721721
.chain(core::iter::once(table_name.span()))
722-
.chain(using.iter().map(|i| i.span))
723-
.chain(columns.iter().map(|i| i.span()))
722+
.chain(columns.iter().map(|i| i.column.span()))
724723
.chain(include.iter().map(|i| i.span))
725724
.chain(with.iter().map(|i| i.span()))
726725
.chain(predicate.iter().map(|i| i.span())),

Diff for: src/keywords.rs

+5
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,13 @@ define_keywords!(
137137
BIT,
138138
BLOB,
139139
BLOCK,
140+
BLOOM,
140141
BLOOMFILTER,
141142
BOOL,
142143
BOOLEAN,
143144
BOTH,
144145
BOX,
146+
BRIN,
145147
BROWSE,
146148
BTREE,
147149
BUCKET,
@@ -386,6 +388,8 @@ define_keywords!(
386388
GENERATED,
387389
GEOGRAPHY,
388390
GET,
391+
GIN,
392+
GIST,
389393
GLOBAL,
390394
GRANT,
391395
GRANTED,
@@ -805,6 +809,7 @@ define_keywords!(
805809
SPATIAL,
806810
SPECIFIC,
807811
SPECIFICTYPE,
812+
SPGIST,
808813
SQL,
809814
SQLEXCEPTION,
810815
SQLSTATE,

Diff for: src/parser/mod.rs

+77-15
Original file line numberDiff line numberDiff line change
@@ -3955,6 +3955,18 @@ impl<'a> Parser<'a> {
39553955
true
39563956
}
39573957

3958+
/// If the current token is one of the given `keywords`, returns the keyword
3959+
/// that matches, without consuming the token. Otherwise, returns [`None`].
3960+
#[must_use]
3961+
pub fn peek_one_of_keywords(&self, keywords: &[Keyword]) -> Option<Keyword> {
3962+
for keyword in keywords {
3963+
if self.peek_keyword(*keyword) {
3964+
return Some(*keyword);
3965+
}
3966+
}
3967+
None
3968+
}
3969+
39583970
/// If the current token is one of the given `keywords`, consume the token
39593971
/// and return the keyword that matches. Otherwise, no tokens are consumed
39603972
/// and returns [`None`].
@@ -6406,12 +6418,13 @@ impl<'a> Parser<'a> {
64066418
};
64076419
let table_name = self.parse_object_name(false)?;
64086420
let using = if self.parse_keyword(Keyword::USING) {
6409-
Some(self.parse_identifier()?)
6421+
Some(self.parse_index_type()?)
64106422
} else {
64116423
None
64126424
};
6425+
64136426
self.expect_token(&Token::LParen)?;
6414-
let columns = self.parse_comma_separated(Parser::parse_order_by_expr)?;
6427+
let columns = self.parse_comma_separated(Parser::parse_create_index_expr)?;
64156428
self.expect_token(&Token::RParen)?;
64166429

64176430
let include = if self.parse_keyword(Keyword::INCLUDE) {
@@ -7629,16 +7642,30 @@ impl<'a> Parser<'a> {
76297642
}
76307643

76317644
pub fn parse_index_type(&mut self) -> Result<IndexType, ParserError> {
7632-
if self.parse_keyword(Keyword::BTREE) {
7633-
Ok(IndexType::BTree)
7645+
Ok(if self.parse_keyword(Keyword::BTREE) {
7646+
IndexType::BTree
76347647
} else if self.parse_keyword(Keyword::HASH) {
7635-
Ok(IndexType::Hash)
7636-
} else {
7637-
self.expected("index type {BTREE | HASH}", self.peek_token())
7638-
}
7648+
IndexType::Hash
7649+
} else if self.parse_keyword(Keyword::GIN) {
7650+
IndexType::GIN
7651+
} else if self.parse_keyword(Keyword::GIST) {
7652+
IndexType::GiST
7653+
} else if self.parse_keyword(Keyword::SPGIST) {
7654+
IndexType::SPGiST
7655+
} else if self.parse_keyword(Keyword::BRIN) {
7656+
IndexType::BRIN
7657+
} else if self.parse_keyword(Keyword::BLOOM) {
7658+
IndexType::Bloom
7659+
} else {
7660+
IndexType::Custom(self.parse_identifier()?)
7661+
})
76397662
}
76407663

7641-
/// Parse [USING {BTREE | HASH}]
7664+
/// Optionally parse the `USING` keyword, followed by an [IndexType]
7665+
/// Example:
7666+
/// ```sql
7667+
//// USING BTREE (name, age DESC)
7668+
/// ```
76427669
pub fn parse_optional_using_then_index_type(
76437670
&mut self,
76447671
) -> Result<Option<IndexType>, ParserError> {
@@ -13631,10 +13658,42 @@ impl<'a> Parser<'a> {
1363113658
}
1363213659
}
1363313660

13634-
/// Parse an expression, optionally followed by ASC or DESC (used in ORDER BY)
13661+
/// Parse an [OrderByExpr] expression.
1363513662
pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, ParserError> {
13663+
self.parse_order_by_expr_inner(false)
13664+
.map(|(order_by, _)| order_by)
13665+
}
13666+
13667+
/// Parse an [IndexColumn].
13668+
pub fn parse_create_index_expr(&mut self) -> Result<IndexColumn, ParserError> {
13669+
self.parse_order_by_expr_inner(true)
13670+
.map(|(column, operator_class)| IndexColumn {
13671+
column,
13672+
operator_class,
13673+
})
13674+
}
13675+
13676+
fn parse_order_by_expr_inner(
13677+
&mut self,
13678+
with_operator_class: bool,
13679+
) -> Result<(OrderByExpr, Option<Ident>), ParserError> {
1363613680
let expr = self.parse_expr()?;
1363713681

13682+
let operator_class: Option<Ident> = if with_operator_class {
13683+
// We check that if non of the following keywords are present, then we parse an
13684+
// identifier as operator class.
13685+
if self
13686+
.peek_one_of_keywords(&[Keyword::ASC, Keyword::DESC, Keyword::NULLS, Keyword::WITH])
13687+
.is_some()
13688+
{
13689+
None
13690+
} else {
13691+
self.maybe_parse(|parser| parser.parse_identifier())?
13692+
}
13693+
} else {
13694+
None
13695+
};
13696+
1363813697
let options = self.parse_order_by_options()?;
1363913698

1364013699
let with_fill = if dialect_of!(self is ClickHouseDialect | GenericDialect)
@@ -13645,11 +13704,14 @@ impl<'a> Parser<'a> {
1364513704
None
1364613705
};
1364713706

13648-
Ok(OrderByExpr {
13649-
expr,
13650-
options,
13651-
with_fill,
13652-
})
13707+
Ok((
13708+
OrderByExpr {
13709+
expr,
13710+
options,
13711+
with_fill,
13712+
},
13713+
operator_class,
13714+
))
1365313715
}
1365413716

1365513717
fn parse_order_by_options(&mut self) -> Result<OrderByOptions, ParserError> {

0 commit comments

Comments
 (0)