Skip to content

Extend support for INDEX parsing #1707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
da57618
Added first tentative tests
LucaCappelletti94 Feb 5, 2025
49e001e
Added support and test to handle operator classes for indices
LucaCappelletti94 Feb 6, 2025
5bb6cb3
Removed
LucaCappelletti94 Feb 6, 2025
30c20f8
Formatted code
LucaCappelletti94 Feb 6, 2025
47ea5d4
Resolved issues regarding serde derive
LucaCappelletti94 Feb 6, 2025
c30853d
Restored the derive for the `Copy` trait
LucaCappelletti94 Feb 8, 2025
2f95334
Merge branch 'main' into gin_trgm_ops
LucaCappelletti94 Feb 8, 2025
8e8608c
Update src/ast/spans.rs
LucaCappelletti94 Feb 11, 2025
7bcd499
Removed operator classes and replaced it with a simpler Ident
LucaCappelletti94 Feb 25, 2025
1709649
Replaced custom index object name with simpler "identifier"
LucaCappelletti94 Feb 25, 2025
9ef6db7
Merge branch 'main' into gin_trgm_ops
LucaCappelletti94 Feb 25, 2025
1049bc0
Fixed errors relative to improper GitHub merge
LucaCappelletti94 Feb 25, 2025
db5c9e7
Fixe clippy code smell
LucaCappelletti94 Feb 25, 2025
8d08bd5
Removed #[cfg_attr(feature = "std", derive(Debug))]
LucaCappelletti94 Feb 25, 2025
bcdde9b
Updtaed the `parse_create_index_expr` index description
LucaCappelletti94 Feb 25, 2025
be9ec88
Added multi-column test
LucaCappelletti94 Feb 25, 2025
71d5bf1
Update src/parser/mod.rs
LucaCappelletti94 Feb 27, 2025
c8e906c
Update src/parser/mod.rs
LucaCappelletti94 Feb 27, 2025
6aba425
Update src/parser/mod.rs
LucaCappelletti94 Feb 27, 2025
57e5c14
Update src/parser/mod.rs
LucaCappelletti94 Feb 27, 2025
e941cb0
Update src/parser/mod.rs
LucaCappelletti94 Feb 27, 2025
653dba0
Update src/parser/mod.rs
LucaCappelletti94 Feb 27, 2025
fd659ff
Replaced bool const for parameter as per issue request
LucaCappelletti94 Feb 27, 2025
f11a614
Extended operator class tests and added test for bloom index syntax
LucaCappelletti94 Mar 2, 2025
7651771
Added a test for the BRIN index type
LucaCappelletti94 Mar 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1119,20 +1119,33 @@ impl fmt::Display for KeyOrIndexDisplay {
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
/// [2]: https://dev.mysql.com/doc/refman/8.0/en/create-index.html
/// [3]: https://www.postgresql.org/docs/14/sql-createindex.html
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum IndexType {
BTree,
Hash,
// TODO add Postgresql's possible indexes
GIN,
GiST,
SPGiST,
BRIN,
Bloom,
/// Users may define their own index types, which would
/// not be covered by the above variants.
Custom(Ident),
}

impl fmt::Display for IndexType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BTree => write!(f, "BTREE"),
Self::Hash => write!(f, "HASH"),
Self::GIN => write!(f, "GIN"),
Self::GiST => write!(f, "GIST"),
Self::SPGiST => write!(f, "SPGIST"),
Self::BRIN => write!(f, "BRIN"),
Self::Bloom => write!(f, "BLOOM"),
Self::Custom(name) => write!(f, "{}", name),
}
}
}
Expand Down
31 changes: 25 additions & 6 deletions src/ast/dml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,31 @@ pub use super::ddl::{ColumnDef, TableConstraint};
use super::{
display_comma_separated, display_separated, query::InputFormatClause, Assignment, ClusteredBy,
CommentDef, Expr, FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
HiveRowFormat, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert,
OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting, SqlOption,
SqliteOnConflict, StorageSerializationPolicy, TableEngine, TableObject, TableWithJoins, Tag,
WrappedCollection,
HiveRowFormat, Ident, IndexType, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit,
OnInsert, OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting,
SqlOption, SqliteOnConflict, StorageSerializationPolicy, TableEngine, TableObject,
TableWithJoins, Tag, WrappedCollection,
};

/// Index column type.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct IndexColumn {
pub column: OrderByExpr,
pub operator_class: Option<Ident>,
}

impl Display for IndexColumn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.column)?;
if let Some(operator_class) = &self.operator_class {
write!(f, " {}", operator_class)?;
}
Ok(())
}
}

/// CREATE INDEX statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand All @@ -49,8 +68,8 @@ pub struct CreateIndex {
pub name: Option<ObjectName>,
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub table_name: ObjectName,
pub using: Option<Ident>,
pub columns: Vec<OrderByExpr>,
pub using: Option<IndexType>,
pub columns: Vec<IndexColumn>,
pub unique: bool,
pub concurrently: bool,
pub if_not_exists: bool,
Expand Down
3 changes: 2 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub use self::ddl::{
TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
ViewColumnDef,
};
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
Expand Down Expand Up @@ -91,6 +91,7 @@ pub use self::value::{

use crate::ast::helpers::key_value_options::KeyValueOptions;
use crate::ast::helpers::stmt_data_loading::{StageLoadSelectItem, StageParamsObject};

#[cfg(feature = "visitor")]
pub use visitor::*;

Expand Down
5 changes: 2 additions & 3 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ impl Spanned for CreateIndex {
let CreateIndex {
name,
table_name,
using,
using: _,
columns,
unique: _, // bool
concurrently: _, // bool
Expand All @@ -719,8 +719,7 @@ impl Spanned for CreateIndex {
name.iter()
.map(|i| i.span())
.chain(core::iter::once(table_name.span()))
.chain(using.iter().map(|i| i.span))
.chain(columns.iter().map(|i| i.span()))
.chain(columns.iter().map(|i| i.column.span()))
.chain(include.iter().map(|i| i.span))
.chain(with.iter().map(|i| i.span()))
.chain(predicate.iter().map(|i| i.span())),
Expand Down
5 changes: 5 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,13 @@ define_keywords!(
BIT,
BLOB,
BLOCK,
BLOOM,
BLOOMFILTER,
BOOL,
BOOLEAN,
BOTH,
BOX,
BRIN,
BROWSE,
BTREE,
BUCKET,
Expand Down Expand Up @@ -386,6 +388,8 @@ define_keywords!(
GENERATED,
GEOGRAPHY,
GET,
GIN,
GIST,
GLOBAL,
GRANT,
GRANTED,
Expand Down Expand Up @@ -802,6 +806,7 @@ define_keywords!(
SPATIAL,
SPECIFIC,
SPECIFICTYPE,
SPGIST,
SQL,
SQLEXCEPTION,
SQLSTATE,
Expand Down
88 changes: 69 additions & 19 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ mod recursion {

use super::ParserError;

#[derive(Debug)]
/// Tracks remaining recursion depth. This value is decremented on
/// each call to [`RecursionCounter::try_decrease()`], when it reaches 0 an error will
/// be returned.
Expand Down Expand Up @@ -263,7 +264,7 @@ impl ParserOptions {
}
}

#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
enum ParserState {
/// The default state of the parser.
Normal,
Expand Down Expand Up @@ -3955,6 +3956,18 @@ impl<'a> Parser<'a> {
true
}

/// If the current token is one of the given `keywords`, returns the keyword
/// that matches, without consuming the token. Otherwise, returns [`None`].
#[must_use]
pub fn peek_one_of_keywords(&self, keywords: &[Keyword]) -> Option<Keyword> {
for keyword in keywords {
if self.peek_keyword(*keyword) {
return Some(*keyword);
}
}
None
}

/// If the current token is one of the given `keywords`, consume the token
/// and return the keyword that matches. Otherwise, no tokens are consumed
/// and returns [`None`].
Expand Down Expand Up @@ -6406,12 +6419,13 @@ impl<'a> Parser<'a> {
};
let table_name = self.parse_object_name(false)?;
let using = if self.parse_keyword(Keyword::USING) {
Some(self.parse_identifier()?)
Some(self.parse_index_type()?)
} else {
None
};

self.expect_token(&Token::LParen)?;
let columns = self.parse_comma_separated(Parser::parse_order_by_expr)?;
let columns = self.parse_comma_separated(Parser::parse_create_index_expr::<true>)?;
self.expect_token(&Token::RParen)?;

let include = if self.parse_keyword(Keyword::INCLUDE) {
Expand Down Expand Up @@ -7629,24 +7643,34 @@ impl<'a> Parser<'a> {
}

pub fn parse_index_type(&mut self) -> Result<IndexType, ParserError> {
if self.parse_keyword(Keyword::BTREE) {
Ok(IndexType::BTree)
Ok(if self.parse_keyword(Keyword::BTREE) {
IndexType::BTree
} else if self.parse_keyword(Keyword::HASH) {
Ok(IndexType::Hash)
} else {
self.expected("index type {BTREE | HASH}", self.peek_token())
}
IndexType::Hash
} else if self.parse_keyword(Keyword::GIN) {
IndexType::GIN
} else if self.parse_keyword(Keyword::GIST) {
IndexType::GiST
} else if self.parse_keyword(Keyword::SPGIST) {
IndexType::SPGiST
} else if self.parse_keyword(Keyword::BRIN) {
IndexType::BRIN
} else if self.parse_keyword(Keyword::BLOOM) {
IndexType::Bloom
} else {
IndexType::Custom(self.parse_identifier()?)
})
}

/// Parse [USING {BTREE | HASH}]
/// Parse [USING {BTREE | HASH | GIN | GIST | SPGIST | BRIN | BLOOM | identifier}]
pub fn parse_optional_using_then_index_type(
&mut self,
) -> Result<Option<IndexType>, ParserError> {
if self.parse_keyword(Keyword::USING) {
Ok(Some(self.parse_index_type()?))
Ok(if self.parse_keyword(Keyword::USING) {
Some(self.parse_index_type()?)
} else {
Ok(None)
}
None
})
}

/// Parse `[ident]`, mostly `ident` is name, like:
Expand Down Expand Up @@ -13595,10 +13619,33 @@ impl<'a> Parser<'a> {
}
}

/// Parse an expression, optionally followed by ASC or DESC (used in ORDER BY)
/// Parse an OrderByExpr expression, optionally followed by ASC or DESC (used in ORDER BY)
pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, ParserError> {
self.parse_create_index_expr::<false>()
.map(|index_column| index_column.column)
}

/// Parse an IndexColumn expression (used in CREATE INDEX)
pub fn parse_create_index_expr<const PARSE_OPERATOR_CLASS: bool>(
&mut self,
) -> Result<IndexColumn, ParserError> {
let expr = self.parse_expr()?;

let operator_class: Option<Ident> = if PARSE_OPERATOR_CLASS {
// We check that if non of the following keywords are present, then we parse an
// identifier as operator class.
if self
.peek_one_of_keywords(&[Keyword::ASC, Keyword::DESC, Keyword::NULLS, Keyword::WITH])
.is_some()
{
None
} else {
self.maybe_parse(|parser| parser.parse_identifier())?
}
} else {
None
};

let options = self.parse_order_by_options()?;

let with_fill = if dialect_of!(self is ClickHouseDialect | GenericDialect)
Expand All @@ -13609,10 +13656,13 @@ impl<'a> Parser<'a> {
None
};

Ok(OrderByExpr {
expr,
options,
with_fill,
Ok(IndexColumn {
column: OrderByExpr {
expr,
options,
with_fill,
},
operator_class,
})
}

Expand Down
Loading