feat: add unset table options support (#5034)

* feat: add unset table options support

* test: add tests

* chore: update greptime-proto

* chore: add comments
This commit is contained in:
Weny Xu
2024-11-21 11:52:56 +08:00
committed by GitHub
parent 0aab68c23b
commit 14d997e2d1
17 changed files with 329 additions and 154 deletions

View File

@@ -25,7 +25,7 @@ use sqlparser::tokenizer::Token;
use crate::error::{self, InvalidColumnOptionSnafu, Result, SetFulltextOptionSnafu};
use crate::parser::ParserContext;
use crate::parsers::utils::validate_column_fulltext_create_option;
use crate::statements::alter::{AlterTable, AlterTableOperation, ChangeTableOption};
use crate::statements::alter::{AlterTable, AlterTableOperation, TableOption};
use crate::statements::statement::Statement;
use crate::util::parse_option_string;
@@ -50,6 +50,8 @@ impl ParserContext<'_> {
Token::Word(w) => {
if w.value.eq_ignore_ascii_case("MODIFY") {
self.parse_alter_table_modify()?
} else if w.value.eq_ignore_ascii_case("UNSET") {
self.parse_alter_table_unset()?
} else {
match w.keyword {
Keyword::ADD => self.parse_alter_table_add()?,
@@ -87,9 +89,9 @@ impl ParserContext<'_> {
.parse_comma_separated(parse_string_options)
.context(error::SyntaxSnafu)?
.into_iter()
.map(|(key, value)| ChangeTableOption { key, value })
.map(|(key, value)| TableOption { key, value })
.collect();
AlterTableOperation::ChangeTableOptions { options }
AlterTableOperation::SetTableOptions { options }
}
_ => self.expected(
"ADD or DROP or MODIFY or RENAME or SET after ALTER TABLE",
@@ -103,6 +105,18 @@ impl ParserContext<'_> {
Ok(AlterTable::new(table_name, alter_operation))
}
fn parse_alter_table_unset(&mut self) -> Result<AlterTableOperation> {
let _ = self.parser.next_token();
let keys = self
.parser
.parse_comma_separated(parse_string_option_names)
.context(error::SyntaxSnafu)?
.into_iter()
.collect();
Ok(AlterTableOperation::UnsetTableOptions { keys })
}
fn parse_alter_table_add(&mut self) -> Result<AlterTableOperation> {
let _ = self.parser.next_token();
if let Some(constraint) = self
@@ -214,6 +228,7 @@ impl ParserContext<'_> {
}
}
/// Parses a string literal and an optional string literal value.
fn parse_string_options(parser: &mut Parser) -> std::result::Result<(String, String), ParserError> {
let name = parser.parse_literal_string()?;
parser.expect_token(&Token::Eq)?;
@@ -230,6 +245,11 @@ fn parse_string_options(parser: &mut Parser) -> std::result::Result<(String, Str
Ok((name, value))
}
/// Parses a comma separated list of string literals.
fn parse_string_option_names(parser: &mut Parser) -> std::result::Result<String, ParserError> {
parser.parse_literal_string()
}
#[cfg(test)]
mod tests {
use std::assert_matches::assert_matches;
@@ -537,7 +557,7 @@ mod tests {
}
}
fn check_parse_alter_table(sql: &str, expected: &[(&str, &str)]) {
fn check_parse_alter_table_set_options(sql: &str, expected: &[(&str, &str)]) {
let result =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
@@ -546,7 +566,7 @@ mod tests {
unreachable!()
};
assert_eq!("test_table", alter.table_name.0[0].value);
let AlterTableOperation::ChangeTableOptions { options } = &alter.alter_operation else {
let AlterTableOperation::SetTableOptions { options } = &alter.alter_operation else {
unreachable!()
};
@@ -558,18 +578,36 @@ mod tests {
assert_eq!(expected, &res);
}
fn check_parse_alter_table_unset_options(sql: &str, expected: &[&str]) {
let result =
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap();
assert_eq!(1, result.len());
let Statement::Alter(alter) = &result[0] else {
unreachable!()
};
assert_eq!("test_table", alter.table_name.0[0].value);
let AlterTableOperation::UnsetTableOptions { keys } = &alter.alter_operation else {
unreachable!()
};
assert_eq!(sql, alter.to_string());
let res = keys.iter().map(|o| o.to_string()).collect::<Vec<_>>();
assert_eq!(expected, &res);
}
#[test]
fn test_parse_alter_column() {
check_parse_alter_table("ALTER TABLE test_table SET 'a'='A'", &[("a", "A")]);
check_parse_alter_table(
fn test_parse_alter_table_set_options() {
check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'='A'", &[("a", "A")]);
check_parse_alter_table_set_options(
"ALTER TABLE test_table SET 'a'='A','b'='B'",
&[("a", "A"), ("b", "B")],
);
check_parse_alter_table(
check_parse_alter_table_set_options(
"ALTER TABLE test_table SET 'a'='A','b'='B','c'='C'",
&[("a", "A"), ("b", "B"), ("c", "C")],
);
check_parse_alter_table("ALTER TABLE test_table SET 'a'=NULL", &[("a", "")]);
check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'=NULL", &[("a", "")]);
ParserContext::create_with_dialect(
"ALTER TABLE test_table SET a INTEGER",
@@ -579,6 +617,18 @@ mod tests {
.unwrap_err();
}
#[test]
fn test_parse_alter_table_unset_options() {
check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a'", &["a"]);
check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a','b'", &["a", "b"]);
ParserContext::create_with_dialect(
"ALTER TABLE test_table UNSET a INTEGER",
&GreptimeDbDialect {},
ParseOptions::default(),
)
.unwrap_err();
}
#[test]
fn test_parse_alter_column_fulltext() {
let sql = "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT WITH(analyzer='English',case_sensitive='false')";

View File

@@ -71,18 +71,29 @@ pub enum AlterTableOperation {
target_type: DataType,
},
/// `SET <table attrs key> = <table attr value>`
ChangeTableOptions { options: Vec<ChangeTableOption> },
SetTableOptions {
options: Vec<TableOption>,
},
UnsetTableOptions {
keys: Vec<String>,
},
/// `DROP COLUMN <name>`
DropColumn { name: Ident },
DropColumn {
name: Ident,
},
/// `RENAME <new_table_name>`
RenameTable { new_table_name: String },
RenameTable {
new_table_name: String,
},
/// `MODIFY COLUMN <column_name> SET FULLTEXT [WITH <options>]`
SetColumnFulltext {
column_name: Ident,
options: FulltextOptions,
},
/// `MODIFY COLUMN <column_name> UNSET FULLTEXT`
UnsetColumnFulltext { column_name: Ident },
UnsetColumnFulltext {
column_name: Ident,
},
}
impl Display for AlterTableOperation {
@@ -109,10 +120,10 @@ impl Display for AlterTableOperation {
} => {
write!(f, r#"MODIFY COLUMN {column_name} {target_type}"#)
}
AlterTableOperation::ChangeTableOptions { options } => {
AlterTableOperation::SetTableOptions { options } => {
let kvs = options
.iter()
.map(|ChangeTableOption { key, value }| {
.map(|TableOption { key, value }| {
if !value.is_empty() {
format!("'{key}'='{value}'")
} else {
@@ -121,9 +132,11 @@ impl Display for AlterTableOperation {
})
.join(",");
write!(f, "SET {kvs}")?;
Ok(())
write!(f, "SET {kvs}")
}
AlterTableOperation::UnsetTableOptions { keys } => {
let keys = keys.iter().map(|k| format!("'{k}'")).join(",");
write!(f, "UNSET {keys}")
}
AlterTableOperation::SetColumnFulltext {
column_name,
@@ -139,14 +152,14 @@ impl Display for AlterTableOperation {
}
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)]
pub struct ChangeTableOption {
pub struct TableOption {
pub key: String,
pub value: String,
}
impl From<ChangeTableOption> for v1::ChangeTableOption {
fn from(c: ChangeTableOption) -> Self {
v1::ChangeTableOption {
impl From<TableOption> for v1::TableOption {
fn from(c: TableOption) -> Self {
v1::TableOption {
key: c.key,
value: c.value,
}