mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-05-27 10:20:38 +00:00
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:
@@ -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')";
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user