Skip to content
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

feat: Alter column fulltext option #4637

Closed
wants to merge 20 commits into from
Closed
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ etcd-client = { version = "0.13" }
fst = "0.4.7"
futures = "0.3"
futures-util = "0.3"
greptime-proto = { git = "https://github.com/GreptimeTeam/greptime-proto.git", rev = "c437b55725b7f5224fe9d46db21072b4a682ee4b" }
greptime-proto = { git = "https://github.com/irenjj/greptime-proto.git", rev = "6df0c7ba6d0c9acfcf155483d19889ffed3ef99d" }
humantime = "2.1"
humantime-serde = "1.1"
itertools = "0.10"
Expand Down
11 changes: 9 additions & 2 deletions src/common/grpc-expr/src/alter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use api::helper::ColumnDataTypeWrapper;
use api::v1::add_column_location::LocationType;
use api::v1::alter_expr::Kind;
use api::v1::{
column_def, AddColumnLocation as Location, AlterExpr, ChangeColumnTypes, CreateTableExpr,
DropColumns, RenameTable, SemanticType,
column_def, AddColumnLocation as Location, ChangeFulltext, AlterExpr, ChangeColumnTypes,
CreateTableExpr, DropColumns, RenameTable, SemanticType,
};
use common_query::AddColumnLocation;
use datatypes::schema::{ColumnSchema, RawSchema};
Expand Down Expand Up @@ -92,6 +92,13 @@ pub fn alter_expr_to_request(table_id: TableId, expr: AlterExpr) -> Result<Alter
Kind::RenameTable(RenameTable { new_table_name }) => {
AlterKind::RenameTable { new_table_name }
}
Kind::ChangeFulltext(ChangeFulltext {
column_name,
options,
}) => AlterKind::ChangeFulltext {
column_name,
options,
},
};

let request = AlterTableRequest {
Expand Down
1 change: 1 addition & 0 deletions src/common/meta/src/ddl/alter_table/region_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ fn create_proto_alter_kind(
})))
}
Kind::RenameTable(_) => Ok(None),
Kind::ChangeFulltext(x) => Ok(Some(alter_request::Kind::ChangeFulltext(x.clone()))),
}
}

Expand Down
1 change: 1 addition & 0 deletions src/common/meta/src/ddl/alter_table/update_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ impl AlterTableProcedure {
new_info.name = new_table_name.to_string();
}
AlterKind::DropColumns { .. } | AlterKind::ChangeColumnTypes { .. } => {}
AlterKind::ChangeFulltext { .. } => {}
}

Ok(new_info)
Expand Down
57 changes: 57 additions & 0 deletions src/datatypes/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ pub use crate::schema::column_schema::{
pub use crate::schema::constraint::ColumnDefaultConstraint;
pub use crate::schema::raw::RawSchema;

const COLUMN_FULLTEXT_OPT_KEY_ANALYZER: &str = "analyzer";
const COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE: &str = "case_sensitive";

/// Key used to store version number of the schema in metadata.
pub const VERSION_KEY: &str = "greptime:version";

Expand Down Expand Up @@ -300,6 +303,48 @@ fn validate_timestamp_index(column_schemas: &[ColumnSchema], timestamp_index: us
Ok(())
}

pub fn validate_fulltext_options(
fulltext: &mut FulltextOptions,
options: &HashMap<String, String>,
) -> bool {
let mut is_fulltext_key_exist = false;
v0y4g3r marked this conversation as resolved.
Show resolved Hide resolved
if let Some(analyzer) = options.get(COLUMN_FULLTEXT_OPT_KEY_ANALYZER) {
match analyzer.to_ascii_lowercase().as_str() {
"english" => {
fulltext.enable = true;
fulltext.analyzer = FulltextAnalyzer::English;
}
"chinese" => {
fulltext.enable = true;
fulltext.analyzer = FulltextAnalyzer::Chinese;
}
_ => {
return false;
}
}
is_fulltext_key_exist = true;
}

if let Some(case_sensitive) = options.get(COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE) {
match case_sensitive.to_ascii_lowercase().as_str() {
"true" => {
fulltext.enable = true;
fulltext.case_sensitive = true;
}
"false" => {
fulltext.enable = true;
fulltext.case_sensitive = false;
}
_ => {
return false;
}
}
is_fulltext_key_exist = true;
}

is_fulltext_key_exist
}
irenjj marked this conversation as resolved.
Show resolved Hide resolved

pub type SchemaRef = Arc<Schema>;

impl TryFrom<Arc<ArrowSchema>> for Schema {
Expand Down Expand Up @@ -484,4 +529,16 @@ mod tests {
.build()
.is_err());
}

#[test]
fn test_validate_fulltext_options() {
let mut fulltext = FulltextOptions::default();
let options = HashMap::from([(String::from("analyzer"), String::from("English"))]);
assert!(validate_fulltext_options(&mut fulltext, &options));

let mut fulltext = FulltextOptions::default();
let options = HashMap::from([(String::from("analyzer"), String::from("Chinglish"))]);
assert!(!validate_fulltext_options(&mut fulltext, &options));

}
}
15 changes: 11 additions & 4 deletions src/datatypes/src/schema/column_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,19 @@ impl ColumnSchema {
}

pub fn with_fulltext_options(mut self, options: FulltextOptions) -> Result<Self> {
self.metadata.insert(
FULLTEXT_KEY.to_string(),
serde_json::to_string(&options).context(error::SerializeSnafu)?,
);
self.set_fulltext_options(options)?;
Ok(self)
}

pub fn set_fulltext_options(&mut self, options: FulltextOptions) -> Result<()> {
if self.data_type == ConcreteDataType::string_datatype() {
v0y4g3r marked this conversation as resolved.
Show resolved Hide resolved
self.metadata.insert(
FULLTEXT_KEY.to_string(),
serde_json::to_string(&options).context(error::SerializeSnafu)?,
);
}
Ok(())
}
}

impl TryFrom<&Field> for ColumnSchema {
Expand Down
13 changes: 10 additions & 3 deletions src/operator/src/expr_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ use api::helper::ColumnDataTypeWrapper;
use api::v1::alter_expr::Kind;
use api::v1::column_def::options_from_column_schema;
use api::v1::{
AddColumn, AddColumns, AlterExpr, ChangeColumnType, ChangeColumnTypes, ColumnDataType,
ColumnDataTypeExtension, CreateFlowExpr, CreateTableExpr, CreateViewExpr, DropColumn,
DropColumns, ExpireAfter, RenameTable, SemanticType, TableName,
AddColumn, AddColumns, ChangeFulltext, AlterExpr, ChangeColumnType, ChangeColumnTypes,
ColumnDataType, ColumnDataTypeExtension, CreateFlowExpr, CreateTableExpr, CreateViewExpr,
DropColumn, DropColumns, ExpireAfter, RenameTable, SemanticType, TableName,
};
use common_error::ext::BoxedError;
use common_grpc_expr::util::ColumnExpr;
Expand Down Expand Up @@ -483,6 +483,13 @@ pub(crate) fn to_alter_expr(
AlterTableOperation::RenameTable { new_table_name } => Kind::RenameTable(RenameTable {
new_table_name: new_table_name.to_string(),
}),
AlterTableOperation::AlterColumnFulltext {
column_name,
options,
} => Kind::ChangeFulltext(ChangeFulltext {
column_name: column_name.value.to_string(),
options: options.clone().into_map(),
}),
};

Ok(AlterExpr {
Expand Down
74 changes: 66 additions & 8 deletions src/sql/src/parsers/alter_parser.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
Expand All @@ -12,6 +10,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;

use common_query::AddColumnLocation;
use snafu::ResultExt;
use sqlparser::keywords::Keyword;
Expand All @@ -22,6 +22,7 @@ use crate::error::{self, Result};
use crate::parser::ParserContext;
use crate::statements::alter::{AlterTable, AlterTableOperation};
use crate::statements::statement::Statement;
use crate::util::parse_option_string;

impl<'a> ParserContext<'a> {
pub(crate) fn parse_alter(&mut self) -> Result<Statement> {
Expand Down Expand Up @@ -74,13 +75,36 @@ impl<'a> ParserContext<'a> {
)));
}
} else if self.consume_token("MODIFY") {
let _ = self.parser.parse_keyword(Keyword::COLUMN);
if !self.parser.parse_keyword(Keyword::COLUMN) {
return Err(ParserError::ParserError(format!(
"expect keyword COLUMN after ALTER TABLE MODIFY, found {}",
self.parser.peek_token()
)));
}
let column_name = Self::canonicalize_identifier(self.parser.parse_identifier(false)?);
let target_type = self.parser.parse_data_type()?;

AlterTableOperation::ChangeColumnType {
column_name,
target_type,
if self.parser.parse_keyword(Keyword::SET) {
let _ = self.parser.parse_keyword(Keyword::FULLTEXT);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can check the parse result to avoid receiving unexpected input


let options = self
.parser
.parse_options(Keyword::WITH)?
.into_iter()
.map(parse_option_string)
.collect::<Result<HashMap<String, String>>>()
.unwrap();

AlterTableOperation::AlterColumnFulltext {
column_name,
options: options.into(),
}
} else {
let target_type = self.parser.parse_data_type()?;

AlterTableOperation::ChangeColumnType {
column_name,
target_type,
}
}
} else if self.parser.parse_keyword(Keyword::RENAME) {
let new_table_name_obj_raw = self.parse_object_name()?;
Expand All @@ -96,7 +120,7 @@ impl<'a> ParserContext<'a> {
AlterTableOperation::RenameTable { new_table_name }
} else {
return Err(ParserError::ParserError(format!(
"expect keyword ADD or DROP or MODIFY or RENAME after ALTER TABLE, found {}",
"expect keyword ADD or DROP or MODIFY or RENAME or SET after ALTER TABLE, found {}",
self.parser.peek_token()
)));
};
Expand Down Expand Up @@ -406,4 +430,38 @@ mod tests {
_ => unreachable!(),
}
}

#[test]
fn test_parse_alter_change_fulltext() {
let sql = "ALTER TABLE test MODIFY COLUMN message SET FULLTEXT WITH(analyzer = 'Chinese', case_sensitive = 'true');";
let mut result =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
assert_eq!(1, result.len());

let statement = result.remove(0);
assert_matches!(statement, Statement::Alter { .. });
match statement {
Statement::Alter(alter_table) => {
assert_eq!("test", alter_table.table_name().0[0].value);

let alter_operation = alter_table.alter_operation();
assert_matches!(
alter_operation,
AlterTableOperation::AlterColumnFulltext { .. }
);
match alter_operation {
AlterTableOperation::AlterColumnFulltext {
column_name,
options,
} => {
assert_eq!(column_name.value, r#"message"#);
assert_eq!(options.get("analyzer").unwrap(), "Chinese");
}
_ => unreachable!(),
}
}
_ => unreachable!(),
}
}
}
17 changes: 17 additions & 0 deletions src/sql/src/statements/alter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use common_query::AddColumnLocation;
use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName, TableConstraint};
use sqlparser_derive::{Visit, VisitMut};

use crate::statements::OptionMap;

#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)]
pub struct AlterTable {
table_name: ObjectName,
Expand Down Expand Up @@ -71,6 +73,11 @@ pub enum AlterTableOperation {
DropColumn { name: Ident },
/// `RENAME <new_table_name>`
RenameTable { new_table_name: String },
/// `SET COLUMN <column_name> FULLTEXT WITH`
AlterColumnFulltext {
column_name: Ident,
options: OptionMap,
},
}

impl Display for AlterTableOperation {
Expand All @@ -97,6 +104,16 @@ impl Display for AlterTableOperation {
} => {
write!(f, r#"MODIFY COLUMN {column_name} {target_type}"#)
}
AlterTableOperation::AlterColumnFulltext {
column_name,
options,
} => {
write!(
f,
r#"SET COLUMN {column_name} FULLTEXT WITH {:?}"#,
options.to_str_map()
)
}
}
}
}
Expand Down
Loading