mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-05-18 22:10:42 +00:00
feat: add ddl timeout/wait options, repartition WITH parsing, meta-client startup refactor (#7589)
* feat: add ddl request timeouts and unify meta client startup Signed-off-by: WenyXu <wenymedia@gmail.com> * feat: omplement ALTER TABLE repartition DDL options parsing Signed-off-by: WenyXu <wenymedia@gmail.com> * test: add sqlness tests Signed-off-by: WenyXu <wenymedia@gmail.com> * test: add unit tests Signed-off-by: WenyXu <wenymedia@gmail.com> * feat: pass timeout argument to procedure Signed-off-by: WenyXu <wenymedia@gmail.com> * chore: apply suggestions Signed-off-by: WenyXu <wenymedia@gmail.com> * chore: refine comments Signed-off-by: WenyXu <wenymedia@gmail.com> * test: assert timeout Signed-off-by: WenyXu <wenymedia@gmail.com> * chore: apply suggestions Signed-off-by: WenyXu <wenymedia@gmail.com> * chore: update proto Signed-off-by: WenyXu <wenymedia@gmail.com> --------- Signed-off-by: WenyXu <wenymedia@gmail.com>
This commit is contained in:
@@ -30,7 +30,8 @@ use crate::error::{self, InvalidColumnOptionSnafu, Result, SetFulltextOptionSnaf
|
||||
use crate::parser::ParserContext;
|
||||
use crate::parsers::create_parser::INVERTED;
|
||||
use crate::parsers::utils::{
|
||||
validate_column_fulltext_create_option, validate_column_skipping_index_create_option,
|
||||
parse_with_options, validate_column_fulltext_create_option,
|
||||
validate_column_skipping_index_create_option,
|
||||
};
|
||||
use crate::statements::OptionMap;
|
||||
use crate::statements::alter::{
|
||||
@@ -181,7 +182,10 @@ impl ParserContext<'_> {
|
||||
}
|
||||
unexpected => self.unsupported(unexpected.to_string())?,
|
||||
};
|
||||
Ok(AlterTable::new(table_name, alter_operation))
|
||||
|
||||
let options = parse_with_options(&mut self.parser)?;
|
||||
// TODO(weny): Respect the DDL options (e.g., WAIT and TIMEOUT) after the ALTER TABLE statement.
|
||||
Ok(AlterTable::new(table_name, alter_operation, options))
|
||||
}
|
||||
|
||||
fn parse_alter_table_unset(&mut self) -> Result<AlterTableOperation> {
|
||||
@@ -1043,6 +1047,50 @@ ALTER TABLE metrics MERGE PARTITION (
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_alter_table_merge_partition_with_options() {
|
||||
let sql = r#"
|
||||
ALTER TABLE alter_repartition_table MERGE PARTITION (
|
||||
device_id < 100 AND area < 'South',
|
||||
device_id < 100 AND area >= 'South'
|
||||
) WITH (
|
||||
TIMEOUT = '5m',
|
||||
WAIT = false
|
||||
);"#;
|
||||
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::AlterTable { .. });
|
||||
if let Statement::AlterTable(alter_table) = statement {
|
||||
assert_matches!(
|
||||
alter_table.alter_operation(),
|
||||
AlterTableOperation::Repartition { .. }
|
||||
);
|
||||
|
||||
if let AlterTableOperation::Repartition { operation } = alter_table.alter_operation() {
|
||||
assert_eq!(operation.from_exprs.len(), 2);
|
||||
assert_eq!(
|
||||
operation.from_exprs[0].to_string(),
|
||||
"device_id < 100 AND area < 'South'"
|
||||
);
|
||||
assert_eq!(
|
||||
operation.from_exprs[1].to_string(),
|
||||
"device_id < 100 AND area >= 'South'"
|
||||
);
|
||||
assert_eq!(operation.into_exprs.len(), 1);
|
||||
}
|
||||
|
||||
// Verify WITH options are parsed
|
||||
let options = alter_table.options().to_str_map();
|
||||
assert_eq!(options.get("timeout").unwrap(), &"5m");
|
||||
assert_eq!(options.get("wait").unwrap(), &"false");
|
||||
assert_eq!(options.len(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_alter_table_repartition_multiple() {
|
||||
let sql = r#"
|
||||
|
||||
@@ -31,19 +31,19 @@ use sqlparser::keywords::ALL_KEYWORDS;
|
||||
use sqlparser::parser::IsOptional::Mandatory;
|
||||
use sqlparser::parser::{Parser, ParserError};
|
||||
use sqlparser::tokenizer::{Token, TokenWithSpan, Word};
|
||||
use table::requests::{validate_database_option, validate_table_option};
|
||||
use table::requests::validate_database_option;
|
||||
|
||||
use crate::ast::{ColumnDef, Ident, ObjectNamePartExt};
|
||||
use crate::error::{
|
||||
self, InvalidColumnOptionSnafu, InvalidDatabaseOptionSnafu, InvalidIntervalSnafu,
|
||||
InvalidSqlSnafu, InvalidTableOptionSnafu, InvalidTimeIndexSnafu, MissingTimeIndexSnafu, Result,
|
||||
SyntaxSnafu, UnexpectedSnafu, UnsupportedSnafu,
|
||||
InvalidSqlSnafu, InvalidTimeIndexSnafu, MissingTimeIndexSnafu, Result, SyntaxSnafu,
|
||||
UnexpectedSnafu, UnsupportedSnafu,
|
||||
};
|
||||
use crate::parser::{FLOW, ParserContext};
|
||||
use crate::parsers::tql_parser;
|
||||
use crate::parsers::utils::{
|
||||
self, validate_column_fulltext_create_option, validate_column_skipping_index_create_option,
|
||||
validate_column_vector_index_create_option,
|
||||
self, parse_with_options, validate_column_fulltext_create_option,
|
||||
validate_column_skipping_index_create_option, validate_column_vector_index_create_option,
|
||||
};
|
||||
use crate::statements::create::{
|
||||
Column, ColumnExtensions, CreateDatabase, CreateExternalTable, CreateFlow, CreateTable,
|
||||
@@ -447,17 +447,7 @@ impl<'a> ParserContext<'a> {
|
||||
}
|
||||
|
||||
fn parse_create_table_options(&mut self) -> Result<OptionMap> {
|
||||
let options = self
|
||||
.parser
|
||||
.parse_options(Keyword::WITH)
|
||||
.context(SyntaxSnafu)?
|
||||
.into_iter()
|
||||
.map(parse_option_string)
|
||||
.collect::<Result<HashMap<String, OptionValue>>>()?;
|
||||
for key in options.keys() {
|
||||
ensure!(validate_table_option(key), InvalidTableOptionSnafu { key });
|
||||
}
|
||||
Ok(OptionMap::new(options))
|
||||
parse_with_options(&mut self.parser)
|
||||
}
|
||||
|
||||
/// "PARTITION ON COLUMNS (...)" clause
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::Utc;
|
||||
@@ -39,13 +40,18 @@ use datatypes::schema::{
|
||||
};
|
||||
use snafu::{ResultExt, ensure};
|
||||
use sqlparser::dialect::Dialect;
|
||||
use sqlparser::keywords::Keyword;
|
||||
use sqlparser::parser::Parser;
|
||||
use table::requests::validate_table_option;
|
||||
|
||||
use crate::error::{
|
||||
ConvertToLogicalExpressionSnafu, InvalidSqlSnafu, ParseSqlValueSnafu, Result,
|
||||
SimplificationSnafu,
|
||||
ConvertToLogicalExpressionSnafu, InvalidSqlSnafu, InvalidTableOptionSnafu, ParseSqlValueSnafu,
|
||||
Result, SimplificationSnafu, SyntaxSnafu,
|
||||
};
|
||||
use crate::parser::{ParseOptions, ParserContext};
|
||||
use crate::statements::OptionMap;
|
||||
use crate::statements::statement::Statement;
|
||||
use crate::util::{OptionValue, parse_option_string};
|
||||
|
||||
/// Check if the given SQL query is a TQL statement.
|
||||
pub fn is_tql(dialect: &dyn Dialect, sql: &str) -> Result<bool> {
|
||||
@@ -272,6 +278,19 @@ pub fn convert_month_day_nano_to_duration(
|
||||
Ok(std::time::Duration::new(adjusted_seconds, nanos_remainder))
|
||||
}
|
||||
|
||||
pub fn parse_with_options(parser: &mut Parser) -> Result<OptionMap> {
|
||||
let options = parser
|
||||
.parse_options(Keyword::WITH)
|
||||
.context(SyntaxSnafu)?
|
||||
.into_iter()
|
||||
.map(parse_option_string)
|
||||
.collect::<Result<HashMap<String, OptionValue>>>()?;
|
||||
for key in options.keys() {
|
||||
ensure!(validate_table_option(key), InvalidTableOptionSnafu { key });
|
||||
}
|
||||
Ok(OptionMap::new(options))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -25,17 +25,26 @@ use serde::Serialize;
|
||||
use sqlparser::ast::{ColumnDef, DataType, Expr, Ident, ObjectName, TableConstraint};
|
||||
use sqlparser_derive::{Visit, VisitMut};
|
||||
|
||||
use crate::statements::OptionMap;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
|
||||
pub struct AlterTable {
|
||||
pub table_name: ObjectName,
|
||||
pub alter_operation: AlterTableOperation,
|
||||
/// Table options in `WITH`. All keys are lowercase.
|
||||
pub options: OptionMap,
|
||||
}
|
||||
|
||||
impl AlterTable {
|
||||
pub(crate) fn new(table_name: ObjectName, alter_operation: AlterTableOperation) -> Self {
|
||||
pub(crate) fn new(
|
||||
table_name: ObjectName,
|
||||
alter_operation: AlterTableOperation,
|
||||
options: OptionMap,
|
||||
) -> Self {
|
||||
Self {
|
||||
table_name,
|
||||
alter_operation,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +56,10 @@ impl AlterTable {
|
||||
&self.alter_operation
|
||||
}
|
||||
|
||||
pub fn options(&self) -> &OptionMap {
|
||||
&self.options
|
||||
}
|
||||
|
||||
pub fn alter_operation_mut(&mut self) -> &mut AlterTableOperation {
|
||||
&mut self.alter_operation
|
||||
}
|
||||
|
||||
@@ -90,6 +90,9 @@ impl OptionMap {
|
||||
self.options.len() + self.secrets.len()
|
||||
}
|
||||
|
||||
/// Convert the option map to a string map.
|
||||
///
|
||||
/// Notes: Not all values can be converted to a string, refer to [OptionValue::expr_as_string] for more details.
|
||||
pub fn to_str_map(&self) -> HashMap<&str, &str> {
|
||||
let mut map = HashMap::with_capacity(self.len());
|
||||
map.extend(
|
||||
@@ -105,6 +108,9 @@ impl OptionMap {
|
||||
map
|
||||
}
|
||||
|
||||
/// Convert the option map to a string map.
|
||||
///
|
||||
/// Notes: Not all values can be converted to a string, refer to [OptionValue::expr_as_string] for more details.
|
||||
pub fn into_map(self) -> HashMap<String, String> {
|
||||
let mut map = HashMap::with_capacity(self.len());
|
||||
map.extend(
|
||||
|
||||
@@ -87,6 +87,7 @@ impl OptionValue {
|
||||
| Value::HexStringLiteral(s) => Some(s),
|
||||
Value::DollarQuotedString(s) => Some(&s.value),
|
||||
Value::Number(s, _) => Some(s),
|
||||
Value::Boolean(b) => Some(if *b { "true" } else { "false" }),
|
||||
_ => None,
|
||||
},
|
||||
Expr::Identifier(ident) => Some(&ident.value),
|
||||
@@ -94,6 +95,9 @@ impl OptionValue {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the option value to a string.
|
||||
///
|
||||
/// Notes: Not all values can be converted to a string, refer to [Self::expr_as_string] for more details.
|
||||
pub fn as_string(&self) -> Option<&str> {
|
||||
Self::expr_as_string(&self.0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user