feat!: support alter skipping index (#5538)

* feat: support alter skipping index

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

* update test results

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

* cargo fmt

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

* update sqlness result

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

* finalize

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

---------

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>
This commit is contained in:
Ruihang Xia
2025-02-14 10:43:21 -08:00
committed by GitHub
parent 1e6d2fb1fa
commit 7fc935c61c
18 changed files with 729 additions and 56 deletions

View File

@@ -25,7 +25,9 @@ use sqlparser::tokenizer::{Token, TokenWithLocation};
use crate::error::{self, InvalidColumnOptionSnafu, Result, SetFulltextOptionSnafu};
use crate::parser::ParserContext;
use crate::parsers::create_parser::INVERTED;
use crate::parsers::utils::validate_column_fulltext_create_option;
use crate::parsers::utils::{
validate_column_fulltext_create_option, validate_column_skipping_index_create_option,
};
use crate::statements::alter::{
AddColumn, AlterDatabase, AlterDatabaseOperation, AlterTable, AlterTableOperation,
KeyValueOption, SetIndexOperation, UnsetIndexOperation,
@@ -241,9 +243,14 @@ impl ParserContext<'_> {
TokenWithLocation {
token: Token::Word(w),
..
} if w.keyword == Keyword::FULLTEXT => Ok(AlterTableOperation::UnsetIndex {
options: UnsetIndexOperation::Fulltext { column_name },
}),
} if w.keyword == Keyword::FULLTEXT => {
self.parser
.expect_keyword(Keyword::INDEX)
.context(error::SyntaxSnafu)?;
Ok(AlterTableOperation::UnsetIndex {
options: UnsetIndexOperation::Fulltext { column_name },
})
}
TokenWithLocation {
token: Token::Word(w),
@@ -256,8 +263,24 @@ impl ParserContext<'_> {
options: UnsetIndexOperation::Inverted { column_name },
})
}
TokenWithLocation {
token: Token::Word(w),
..
} if w.value.eq_ignore_ascii_case("SKIPPING") => {
self.parser
.expect_keyword(Keyword::INDEX)
.context(error::SyntaxSnafu)?;
Ok(AlterTableOperation::UnsetIndex {
options: UnsetIndexOperation::Skipping { column_name },
})
}
_ => self.expected(
format!("{:?} OR INVERTED INDEX", Keyword::FULLTEXT).as_str(),
format!(
"{:?} OR INVERTED INDEX OR SKIPPING INDEX",
Keyword::FULLTEXT
)
.as_str(),
self.parser.peek_token(),
),
}
@@ -268,7 +291,12 @@ impl ParserContext<'_> {
TokenWithLocation {
token: Token::Word(w),
..
} if w.keyword == Keyword::FULLTEXT => self.parse_alter_column_fulltext(column_name),
} if w.keyword == Keyword::FULLTEXT => {
self.parser
.expect_keyword(Keyword::INDEX)
.context(error::SyntaxSnafu)?;
self.parse_alter_column_fulltext(column_name)
}
TokenWithLocation {
token: Token::Word(w),
@@ -281,8 +309,18 @@ impl ParserContext<'_> {
options: SetIndexOperation::Inverted { column_name },
})
}
TokenWithLocation {
token: Token::Word(w),
..
} if w.value.eq_ignore_ascii_case("SKIPPING") => {
self.parser
.expect_keyword(Keyword::INDEX)
.context(error::SyntaxSnafu)?;
self.parse_alter_column_skipping(column_name)
}
_ => self.expected(
format!("{:?} OR INVERTED INDEX", Keyword::FULLTEXT).as_str(),
format!("{:?} OR INVERTED OR SKIPPING INDEX", Keyword::FULLTEXT).as_str(),
self.parser.peek_token(),
),
}
@@ -319,6 +357,35 @@ impl ParserContext<'_> {
},
})
}
fn parse_alter_column_skipping(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
let options = self
.parser
.parse_options(Keyword::WITH)
.context(error::SyntaxSnafu)?
.into_iter()
.map(parse_option_string)
.collect::<Result<HashMap<String, String>>>()?;
for key in options.keys() {
ensure!(
validate_column_skipping_index_create_option(key),
InvalidColumnOptionSnafu {
name: column_name.to_string(),
msg: format!("invalid SKIPPING INDEX option: {key}"),
}
);
}
Ok(AlterTableOperation::SetIndex {
options: SetIndexOperation::Skipping {
column_name,
options: options
.try_into()
.context(error::SetSkippingIndexOptionSnafu)?,
},
})
}
}
/// Parses a string literal and an optional string literal value.
@@ -891,7 +958,7 @@ mod tests {
#[test]
fn test_parse_alter_column_fulltext() {
let sql = "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT WITH(analyzer='English',case_sensitive='false')";
let sql = "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer='English',case_sensitive='false')";
let mut result =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
@@ -928,7 +995,7 @@ mod tests {
_ => unreachable!(),
}
let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET FULLTEXT";
let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET FULLTEXT INDEX";
let mut result =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
@@ -955,7 +1022,8 @@ mod tests {
_ => unreachable!(),
}
let invalid_sql = "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT WITH('abcd'='true')";
let invalid_sql =
"ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH('abcd'='true')";
let result = ParserContext::create_with_dialect(
invalid_sql,
&GreptimeDbDialect {},

View File

@@ -16,7 +16,7 @@ use std::fmt::{Debug, Display};
use api::v1;
use common_query::AddColumnLocation;
use datatypes::schema::FulltextOptions;
use datatypes::schema::{FulltextOptions, SkippingIndexOptions};
use itertools::Itertools;
use serde::Serialize;
use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName, TableConstraint};
@@ -96,22 +96,28 @@ pub enum AlterTableOperation {
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
pub enum SetIndexOperation {
/// `MODIFY COLUMN <column_name> SET FULLTEXT [WITH <options>]`
/// `MODIFY COLUMN <column_name> SET FULLTEXT INDEX [WITH <options>]`
Fulltext {
column_name: Ident,
options: FulltextOptions,
},
/// `MODIFY COLUMN <column_name> SET INVERTED INDEX`
Inverted { column_name: Ident },
/// `MODIFY COLUMN <column_name> SET SKIPPING INDEX`
Skipping {
column_name: Ident,
options: SkippingIndexOptions,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
pub enum UnsetIndexOperation {
/// `MODIFY COLUMN <column_name> UNSET FULLTEXT`
/// `MODIFY COLUMN <column_name> UNSET FULLTEXT INDEX`
Fulltext { column_name: Ident },
/// `MODIFY COLUMN <column_name> UNSET INVERTED INDEX`
Inverted { column_name: Ident },
/// `MODIFY COLUMN <column_name> UNSET SKIPPING INDEX`
Skipping { column_name: Ident },
}
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
@@ -175,19 +181,28 @@ impl Display for AlterTableOperation {
column_name,
options,
} => {
write!(f, "MODIFY COLUMN {column_name} SET FULLTEXT WITH(analyzer={0}, case_sensitive={1})", options.analyzer, options.case_sensitive)
write!(f, "MODIFY COLUMN {column_name} SET FULLTEXT INDEX WITH(analyzer={0}, case_sensitive={1})", options.analyzer, options.case_sensitive)
}
SetIndexOperation::Inverted { column_name } => {
write!(f, "MODIFY COLUMN {column_name} SET INVERTED INDEX")
}
SetIndexOperation::Skipping {
column_name,
options,
} => {
write!(f, "MODIFY COLUMN {column_name} SET SKIPPING INDEX WITH(granularity={0}, index_type={1})", options.granularity, options.index_type)
}
},
AlterTableOperation::UnsetIndex { options } => match options {
UnsetIndexOperation::Fulltext { column_name } => {
write!(f, "MODIFY COLUMN {column_name} UNSET FULLTEXT")
write!(f, "MODIFY COLUMN {column_name} UNSET FULLTEXT INDEX")
}
UnsetIndexOperation::Inverted { column_name } => {
write!(f, "MODIFY COLUMN {column_name} UNSET INVERTED INDEX")
}
UnsetIndexOperation::Skipping { column_name } => {
write!(f, "MODIFY COLUMN {column_name} UNSET SKIPPING INDEX")
}
},
}
}
@@ -410,7 +425,7 @@ ALTER TABLE monitor RENAME monitor_new"#,
}
}
let sql = "ALTER TABLE monitor MODIFY COLUMN a SET FULLTEXT WITH(analyzer='English',case_sensitive='false')";
let sql = "ALTER TABLE monitor MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer='English',case_sensitive='false')";
let stmts =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
@@ -422,7 +437,7 @@ ALTER TABLE monitor RENAME monitor_new"#,
let new_sql = format!("\n{}", set);
assert_eq!(
r#"
ALTER TABLE monitor MODIFY COLUMN a SET FULLTEXT WITH(analyzer=English, case_sensitive=false)"#,
ALTER TABLE monitor MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer=English, case_sensitive=false)"#,
&new_sql
);
}
@@ -431,7 +446,7 @@ ALTER TABLE monitor MODIFY COLUMN a SET FULLTEXT WITH(analyzer=English, case_sen
}
}
let sql = "ALTER TABLE monitor MODIFY COLUMN a UNSET FULLTEXT";
let sql = "ALTER TABLE monitor MODIFY COLUMN a UNSET FULLTEXT INDEX";
let stmts =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
@@ -443,7 +458,7 @@ ALTER TABLE monitor MODIFY COLUMN a SET FULLTEXT WITH(analyzer=English, case_sen
let new_sql = format!("\n{}", set);
assert_eq!(
r#"
ALTER TABLE monitor MODIFY COLUMN a UNSET FULLTEXT"#,
ALTER TABLE monitor MODIFY COLUMN a UNSET FULLTEXT INDEX"#,
&new_sql
);
}