Skip to content

Add support column prefix index for MySQL #1732

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

Closed
wants to merge 5 commits into from

Conversation

zzzdong
Copy link
Contributor

@zzzdong zzzdong commented Feb 20, 2025

This pull request add support for column prefixe and functional indexes in MySQL's CREATE TABLE and ALTER TABLE statements.

 ALTER TABLE `tbl_1` ADD KEY `idx_1` (col_1(10) DESC, (LOWER(col_2)));

May fix parts of #302 .

src/ast/mod.rs Outdated
Comment on lines 8624 to 8637
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum IndexExpr {
Column(Ident),
/// Mysql specific syntax
///
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.3/en/create-index.html)
/// for more details.
ColumnPrefix {
column: Ident,
length: u64,
},
Functional(Box<Expr>),
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be possible to replace this with an Expr instead? The docs seems to suggest that arbitrary expressions are allowed, from the docs

CREATE TABLE t1 (col1 INT, col2 INT, INDEX func_index ((ABS(col1))));
CREATE INDEX idx1 ON t1 ((col1 + col2));
CREATE INDEX idx2 ON t1 ((col1 + col2), (col1 - col2), col1);
ALTER TABLE t1 ADD INDEX ((col1 * 40) DESC);

this would let us reuse existing code and representation, maybe even replace IndexField with OrderByExpr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that using OrderByExpr could be a good approach. However, I am somewhat concerned that users might find it challenging to distinguish between the different Expr when working with it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The different Expr I imagine are encoded by the Expr variants, I think it would be preferrable to using a custom repr here which looks to require duplicating a lot of Expr in order to be comprehensive

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right. I believe adding only ColumnPrefix will suffice. We can extend the Expr enum as follows:

enum Expr {
    ColumnPrefix { column: Ident, length: u64 }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm it looks like ColumnPrefix is equivalent to a function call with one argument? that can already be expressed by an Expr so that it doesn't seem like that should be necessary?

@zzzdong zzzdong force-pushed the feature/column-prefix-index branch from 3ea8ed2 to a362ed7 Compare February 24, 2025 03:21
@zzzdong zzzdong force-pushed the feature/column-prefix-index branch from 1613acf to d2092d6 Compare February 25, 2025 02:41
Comment on lines +7649 to +7653
pub fn parse_index_exprs(&mut self) -> Result<Vec<IndexExpr>, ParserError> {
self.parse_parenthesized(|p| p.parse_comma_separated(Parser::parse_index_expr))
}

pub fn parse_index_expr(&mut self) -> Result<IndexExpr, ParserError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is flagged as a public function can we add a short description mentioning what it does?

let order_options = self.parse_order_by_options()?;
(None, order_options)
} else {
let operator_class = self.maybe_parse(|p| p.parse_expr())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I couldn't see operator_class being used in the introduced tests or documented so its unclear what the syntax is?


let (operator_class, order_options) = if self.peek_keyword(Keyword::ASC)
|| self.peek_keyword(Keyword::DESC)
|| self.peek_keyword(Keyword::NULLS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we introduce a test case that covers the ASC/DESC and nulls_first syntax?

Comment on lines +7661 to +7663
let (operator_class, order_options) = if self.peek_keyword(Keyword::ASC)
|| self.peek_keyword(Keyword::DESC)
|| self.peek_keyword(Keyword::NULLS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using peek, we can probably use self.maybe_parse() so that we don't need to maintain this list of keywords in sync with the parse_order_by_options function e.g.

let (operator_class, order_options) = if let Some(order_options ) = self.maybe_parse(|p| p.parse_order_by_options())? {
  (None, order_options)
} else {
  let operator_class = self.maybe_parse(|p| p.parse_expr())?;
  let order_options = self.parse_order_by_options()?;
  (operator_class, order_options)
};

Comment on lines +14724 to +14725
use std::vec;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm is this needed?

@zzzdong
Copy link
Contributor Author

zzzdong commented Feb 25, 2025

Considering the work on the index parsing in #1707, this will be closed.

@zzzdong zzzdong closed this Feb 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants