mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2025-12-22 22:20:02 +00:00
feat: add SET DEFAULT syntax (#6421)
* feat: add `SET DEFAULT` syntax Signed-off-by: Yihai Lin <yihai-lin@foxmail.com> * test: add `CURRENT_TIMESTAMP()` as default value for `SET DEFAULT` syntax Signed-off-by: Yihai Lin <yihai-lin@foxmail.com> * refactor: Make the error types more precise. Signed-off-by: Yihai Lin <yihai-lin@foxmail.com> * chore: a minor error display enchancement for `SET DEFAULT` Signed-off-by: Yihai Lin <yihai-lin@foxmail.com> * refactor: Using `MODIFY COLUMN` for `DROP/SET DEFUALT` Signed-off-by: Yihai Lin <yihai-lin@foxmail.com> * chore: update `greptime-proto` Signed-off-by: Yihai Lin <yihai-lin@foxmail.com> --------- Signed-off-by: Yihai Lin <yihai-lin@foxmail.com>
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -2458,6 +2458,7 @@ dependencies = [
|
||||
"common-error",
|
||||
"common-macro",
|
||||
"common-query",
|
||||
"common-sql",
|
||||
"common-time",
|
||||
"datatypes",
|
||||
"paste",
|
||||
@@ -2722,6 +2723,7 @@ dependencies = [
|
||||
"datatypes",
|
||||
"hex",
|
||||
"jsonb",
|
||||
"serde_json",
|
||||
"snafu 0.8.5",
|
||||
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
||||
]
|
||||
@@ -5226,7 +5228,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "greptime-proto"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/GreptimeTeam/greptime-proto.git?rev=7fcaa3e413947a7a28d9af95812af26c1939ce78#7fcaa3e413947a7a28d9af95812af26c1939ce78"
|
||||
source = "git+https://github.com/GreptimeTeam/greptime-proto.git?rev=3bb33593a781504e025e6315572bc5dfdc1dc497#3bb33593a781504e025e6315572bc5dfdc1dc497"
|
||||
dependencies = [
|
||||
"prost 0.13.5",
|
||||
"serde",
|
||||
@@ -12152,6 +12154,7 @@ dependencies = [
|
||||
"common-macro",
|
||||
"common-meta",
|
||||
"common-recordbatch",
|
||||
"common-sql",
|
||||
"common-time",
|
||||
"common-wal",
|
||||
"datafusion-expr",
|
||||
@@ -12166,6 +12169,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"snafu 0.8.5",
|
||||
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
||||
"strum 0.27.1",
|
||||
"tokio",
|
||||
]
|
||||
@@ -12514,6 +12518,7 @@ dependencies = [
|
||||
"common-macro",
|
||||
"common-query",
|
||||
"common-recordbatch",
|
||||
"common-sql",
|
||||
"common-telemetry",
|
||||
"common-test-util",
|
||||
"common-time",
|
||||
@@ -12534,6 +12539,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"snafu 0.8.5",
|
||||
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
||||
"store-api",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
||||
@@ -140,7 +140,7 @@ etcd-client = "0.14"
|
||||
fst = "0.4.7"
|
||||
futures = "0.3"
|
||||
futures-util = "0.3"
|
||||
greptime-proto = { git = "https://github.com/GreptimeTeam/greptime-proto.git", rev = "7fcaa3e413947a7a28d9af95812af26c1939ce78" }
|
||||
greptime-proto = { git = "https://github.com/GreptimeTeam/greptime-proto.git", rev = "3bb33593a781504e025e6315572bc5dfdc1dc497" }
|
||||
hex = "0.4"
|
||||
http = "1"
|
||||
humantime = "2.1"
|
||||
|
||||
@@ -14,6 +14,7 @@ common-catalog.workspace = true
|
||||
common-error.workspace = true
|
||||
common-macro.workspace = true
|
||||
common-query.workspace = true
|
||||
common-sql.workspace = true
|
||||
common-time.workspace = true
|
||||
datatypes.workspace = true
|
||||
prost.workspace = true
|
||||
|
||||
@@ -27,16 +27,17 @@ use common_query::AddColumnLocation;
|
||||
use datatypes::schema::{ColumnSchema, FulltextOptions, RawSchema, SkippingIndexOptions};
|
||||
use snafu::{ensure, OptionExt, ResultExt};
|
||||
use store_api::region_request::{SetRegionOption, UnsetRegionOption};
|
||||
use table::metadata::TableId;
|
||||
use table::metadata::{TableId, TableMeta};
|
||||
use table::requests::{
|
||||
AddColumnRequest, AlterKind, AlterTableRequest, ModifyColumnTypeRequest, SetIndexOption,
|
||||
UnsetIndexOption,
|
||||
AddColumnRequest, AlterKind, AlterTableRequest, ModifyColumnTypeRequest, SetDefaultRequest,
|
||||
SetIndexOption, UnsetIndexOption,
|
||||
};
|
||||
|
||||
use crate::error::{
|
||||
InvalidColumnDefSnafu, InvalidIndexOptionSnafu, InvalidSetFulltextOptionRequestSnafu,
|
||||
InvalidSetSkippingIndexOptionRequestSnafu, InvalidSetTableOptionRequestSnafu,
|
||||
InvalidUnsetTableOptionRequestSnafu, MissingAlterIndexOptionSnafu, MissingFieldSnafu,
|
||||
ColumnNotFoundSnafu, InvalidColumnDefSnafu, InvalidIndexOptionSnafu,
|
||||
InvalidSetFulltextOptionRequestSnafu, InvalidSetSkippingIndexOptionRequestSnafu,
|
||||
InvalidSetTableOptionRequestSnafu, InvalidUnsetTableOptionRequestSnafu,
|
||||
MissingAlterIndexOptionSnafu, MissingFieldSnafu, MissingTableMetaSnafu,
|
||||
MissingTimestampColumnSnafu, Result, UnknownLocationTypeSnafu,
|
||||
};
|
||||
|
||||
@@ -97,7 +98,13 @@ fn unset_index_option_from_proto(unset_index: api::v1::UnsetIndex) -> Result<Uns
|
||||
}
|
||||
|
||||
/// Convert an [`AlterTableExpr`] to an [`AlterTableRequest`]
|
||||
pub fn alter_expr_to_request(table_id: TableId, expr: AlterTableExpr) -> Result<AlterTableRequest> {
|
||||
///
|
||||
/// note: `table_meta` must not be None if [`AlterTableExpr`] is `SetDefault`
|
||||
pub fn alter_expr_to_request(
|
||||
table_id: TableId,
|
||||
expr: AlterTableExpr,
|
||||
table_meta: Option<&TableMeta>,
|
||||
) -> Result<AlterTableRequest> {
|
||||
let catalog_name = expr.catalog_name;
|
||||
let schema_name = expr.schema_name;
|
||||
let kind = expr.kind.context(MissingFieldSnafu { field: "kind" })?;
|
||||
@@ -218,6 +225,32 @@ pub fn alter_expr_to_request(table_id: TableId, expr: AlterTableExpr) -> Result<
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
AlterKind::DropDefaults { names }
|
||||
}
|
||||
Kind::SetDefaults(o) => {
|
||||
let table_meta = table_meta.context(MissingTableMetaSnafu { table_id })?;
|
||||
let defaults = o
|
||||
.set_defaults
|
||||
.into_iter()
|
||||
.map(|col| {
|
||||
let column_scheme = table_meta
|
||||
.schema
|
||||
.column_schema_by_name(&col.column_name)
|
||||
.context(ColumnNotFoundSnafu {
|
||||
column_name: &col.column_name,
|
||||
})?;
|
||||
let default_constraint = common_sql::convert::deserialize_default_constraint(
|
||||
col.default_constraint.as_slice(),
|
||||
&col.column_name,
|
||||
&column_scheme.data_type,
|
||||
)
|
||||
.context(crate::error::SqlCommonSnafu)?;
|
||||
Ok(SetDefaultRequest {
|
||||
column_name: col.column_name,
|
||||
default_constraint,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
AlterKind::SetDefaults { defaults }
|
||||
}
|
||||
};
|
||||
|
||||
let request = AlterTableRequest {
|
||||
@@ -317,7 +350,7 @@ mod tests {
|
||||
})),
|
||||
};
|
||||
|
||||
let alter_request = alter_expr_to_request(1, expr).unwrap();
|
||||
let alter_request = alter_expr_to_request(1, expr, None).unwrap();
|
||||
assert_eq!(alter_request.catalog_name, "");
|
||||
assert_eq!(alter_request.schema_name, "");
|
||||
assert_eq!("monitor".to_string(), alter_request.table_name);
|
||||
@@ -381,7 +414,7 @@ mod tests {
|
||||
})),
|
||||
};
|
||||
|
||||
let alter_request = alter_expr_to_request(1, expr).unwrap();
|
||||
let alter_request = alter_expr_to_request(1, expr, None).unwrap();
|
||||
assert_eq!(alter_request.catalog_name, "");
|
||||
assert_eq!(alter_request.schema_name, "");
|
||||
assert_eq!("monitor".to_string(), alter_request.table_name);
|
||||
@@ -433,7 +466,7 @@ mod tests {
|
||||
})),
|
||||
};
|
||||
|
||||
let alter_request = alter_expr_to_request(1, expr).unwrap();
|
||||
let alter_request = alter_expr_to_request(1, expr, None).unwrap();
|
||||
assert_eq!(alter_request.catalog_name, "test_catalog");
|
||||
assert_eq!(alter_request.schema_name, "test_schema");
|
||||
assert_eq!("monitor".to_string(), alter_request.table_name);
|
||||
@@ -465,7 +498,7 @@ mod tests {
|
||||
})),
|
||||
};
|
||||
|
||||
let alter_request = alter_expr_to_request(1, expr).unwrap();
|
||||
let alter_request = alter_expr_to_request(1, expr, None).unwrap();
|
||||
assert_eq!(alter_request.catalog_name, "test_catalog");
|
||||
assert_eq!(alter_request.schema_name, "test_schema");
|
||||
assert_eq!("monitor".to_string(), alter_request.table_name);
|
||||
|
||||
@@ -161,6 +161,27 @@ pub enum Error {
|
||||
#[snafu(source)]
|
||||
error: datatypes::error::Error,
|
||||
},
|
||||
|
||||
#[snafu(display("Sql common error"))]
|
||||
SqlCommon {
|
||||
source: common_sql::error::Error,
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
|
||||
#[snafu(display("Missing required field in protobuf, column name: {}", column_name))]
|
||||
ColumnNotFound {
|
||||
column_name: String,
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
|
||||
#[snafu(display("Need table metadata, but not found, table_id: {}", table_id))]
|
||||
MissingTableMeta {
|
||||
table_id: u32,
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -190,6 +211,9 @@ impl ErrorExt for Error {
|
||||
| Error::InvalidSetSkippingIndexOptionRequest { .. }
|
||||
| Error::MissingAlterIndexOption { .. }
|
||||
| Error::InvalidIndexOption { .. } => StatusCode::InvalidArguments,
|
||||
Error::ColumnNotFound { .. } => StatusCode::TableColumnNotFound,
|
||||
Error::SqlCommon { source, .. } => source.status_code(),
|
||||
Error::MissingTableMeta { .. } => StatusCode::Unexpected,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,9 +94,12 @@ impl AlterLogicalTablesProcedure {
|
||||
let table_info = TableInfo::try_from(table.table_info.clone())
|
||||
.context(error::ConvertRawTableInfoSnafu)?;
|
||||
let table_ref = task.table_ref();
|
||||
let request =
|
||||
alter_expr_to_request(table.table_info.ident.table_id, task.alter_table.clone())
|
||||
.context(ConvertAlterTableRequestSnafu)?;
|
||||
let request = alter_expr_to_request(
|
||||
table.table_info.ident.table_id,
|
||||
task.alter_table.clone(),
|
||||
Some(&table_info.meta),
|
||||
)
|
||||
.context(ConvertAlterTableRequestSnafu)?;
|
||||
let new_meta = table_info
|
||||
.meta
|
||||
.builder_with_alter_kind(table_ref.table, &request.alter_kind)
|
||||
|
||||
@@ -270,7 +270,7 @@ fn build_new_table_info(
|
||||
let catalog_name = &table_info.catalog_name;
|
||||
let table_name = &table_info.name;
|
||||
let table_id = table_info.ident.table_id;
|
||||
let request = alter_expr_to_request(table_id, alter_table_expr)
|
||||
let request = alter_expr_to_request(table_id, alter_table_expr, Some(&table_info.meta))
|
||||
.context(error::ConvertAlterTableRequestSnafu)?;
|
||||
|
||||
let new_meta = table_info
|
||||
@@ -302,6 +302,7 @@ fn build_new_table_info(
|
||||
| AlterKind::SetIndexes { .. }
|
||||
| AlterKind::UnsetIndexes { .. }
|
||||
| AlterKind::DropDefaults { .. } => {}
|
||||
AlterKind::SetDefaults { .. } => {}
|
||||
}
|
||||
|
||||
info!(
|
||||
|
||||
@@ -111,6 +111,7 @@ fn create_proto_alter_kind(
|
||||
Kind::SetIndexes(v) => Ok(Some(alter_request::Kind::SetIndexes(v.clone()))),
|
||||
Kind::UnsetIndexes(v) => Ok(Some(alter_request::Kind::UnsetIndexes(v.clone()))),
|
||||
Kind::DropDefaults(v) => Ok(Some(alter_request::Kind::DropDefaults(v.clone()))),
|
||||
Kind::SetDefaults(v) => Ok(Some(alter_request::Kind::SetDefaults(v.clone()))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ datafusion-sql.workspace = true
|
||||
datatypes.workspace = true
|
||||
hex = "0.4"
|
||||
jsonb.workspace = true
|
||||
serde_json.workspace = true
|
||||
snafu.workspace = true
|
||||
sqlparser.workspace = true
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ use std::str::FromStr;
|
||||
use common_time::timezone::Timezone;
|
||||
use common_time::Timestamp;
|
||||
use datatypes::prelude::ConcreteDataType;
|
||||
use datatypes::schema::ColumnDefaultConstraint;
|
||||
use datatypes::types::{parse_string_to_json_type_value, parse_string_to_vector_type_value};
|
||||
use datatypes::value::{OrderedF32, OrderedF64, Value};
|
||||
use snafu::{ensure, OptionExt, ResultExt};
|
||||
@@ -29,8 +30,8 @@ pub use sqlparser::ast::{
|
||||
|
||||
use crate::error::{
|
||||
ColumnTypeMismatchSnafu, ConvertSqlValueSnafu, ConvertStrSnafu, DatatypeSnafu,
|
||||
InvalidCastSnafu, InvalidSqlValueSnafu, InvalidUnaryOpSnafu, ParseSqlValueSnafu, Result,
|
||||
TimestampOverflowSnafu, UnsupportedUnaryOpSnafu,
|
||||
DeserializeSnafu, InvalidCastSnafu, InvalidSqlValueSnafu, InvalidUnaryOpSnafu,
|
||||
ParseSqlValueSnafu, Result, TimestampOverflowSnafu, UnsupportedUnaryOpSnafu,
|
||||
};
|
||||
|
||||
fn parse_sql_number<R: FromStr + std::fmt::Debug>(n: &str) -> Result<R>
|
||||
@@ -368,6 +369,27 @@ pub(crate) fn parse_hex_string(s: &str) -> Result<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize default constraint from json bytes
|
||||
pub fn deserialize_default_constraint(
|
||||
bytes: &[u8],
|
||||
column_name: &str,
|
||||
data_type: &ConcreteDataType,
|
||||
) -> Result<Option<ColumnDefaultConstraint>> {
|
||||
let json = String::from_utf8_lossy(bytes);
|
||||
let default_constraint = serde_json::from_str(&json).context(DeserializeSnafu { json })?;
|
||||
let column_def = sqlparser::ast::ColumnOptionDef {
|
||||
name: None,
|
||||
option: sqlparser::ast::ColumnOption::Default(default_constraint),
|
||||
};
|
||||
|
||||
crate::default_constraint::parse_column_default_constraint(
|
||||
column_name,
|
||||
data_type,
|
||||
&[column_def],
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use common_base::bytes::Bytes;
|
||||
|
||||
@@ -55,7 +55,7 @@ pub enum Error {
|
||||
},
|
||||
|
||||
#[snafu(display(
|
||||
"Unsupported expr in default constraint: {:?} for column: {}",
|
||||
"Unsupported expr in default constraint: {} for column: {}",
|
||||
expr,
|
||||
column_name
|
||||
))]
|
||||
@@ -131,6 +131,15 @@ pub enum Error {
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
|
||||
#[snafu(display("Failed to deserialize data, json: {}", json))]
|
||||
Deserialize {
|
||||
#[snafu(source)]
|
||||
error: serde_json::Error,
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
json: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl ErrorExt for Error {
|
||||
@@ -147,6 +156,8 @@ impl ErrorExt for Error {
|
||||
| InvalidCast { .. }
|
||||
| ConvertStr { .. }
|
||||
| TimestampOverflow { .. } => StatusCode::InvalidArguments,
|
||||
Deserialize { .. } => StatusCode::Unexpected,
|
||||
|
||||
Datatype { source, .. } => source.status_code(),
|
||||
ConvertSqlValue { .. } => StatusCode::Unsupported,
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ use api::v1::{
|
||||
ColumnDataType, ColumnDataTypeExtension, CreateFlowExpr, CreateTableExpr, CreateViewExpr,
|
||||
DropColumn, DropColumns, DropDefaults, ExpireAfter, FulltextBackend as PbFulltextBackend,
|
||||
ModifyColumnType, ModifyColumnTypes, RenameTable, SemanticType, SetDatabaseOptions,
|
||||
SetFulltext, SetIndex, SetIndexes, SetInverted, SetSkipping, SetTableOptions,
|
||||
SetDefaults, SetFulltext, SetIndex, SetIndexes, SetInverted, SetSkipping, SetTableOptions,
|
||||
SkippingIndexType as PbSkippingIndexType, TableName, UnsetDatabaseOptions, UnsetFulltext,
|
||||
UnsetIndex, UnsetIndexes, UnsetInverted, UnsetSkipping, UnsetTableOptions,
|
||||
};
|
||||
@@ -664,6 +664,21 @@ pub(crate) fn to_alter_table_expr(
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
})
|
||||
}
|
||||
AlterTableOperation::SetDefaults { defaults } => AlterTableKind::SetDefaults(SetDefaults {
|
||||
set_defaults: defaults
|
||||
.into_iter()
|
||||
.map(|col| {
|
||||
let column_name = col.column_name.to_string();
|
||||
let default_constraint = serde_json::to_string(&col.default_constraint)
|
||||
.context(EncodeJsonSnafu)?
|
||||
.into_bytes();
|
||||
Ok(api::v1::SetDefault {
|
||||
column_name,
|
||||
default_constraint,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
}),
|
||||
};
|
||||
|
||||
Ok(AlterTableExpr {
|
||||
|
||||
@@ -1591,7 +1591,8 @@ pub fn verify_alter(
|
||||
expr: AlterTableExpr,
|
||||
) -> Result<bool> {
|
||||
let request: AlterTableRequest =
|
||||
common_grpc_expr::alter_expr_to_request(table_id, expr).context(AlterExprToRequestSnafu)?;
|
||||
common_grpc_expr::alter_expr_to_request(table_id, expr, Some(&table_info.meta))
|
||||
.context(AlterExprToRequestSnafu)?;
|
||||
|
||||
let AlterTableRequest {
|
||||
table_name,
|
||||
|
||||
@@ -30,7 +30,8 @@ use crate::parsers::utils::{
|
||||
};
|
||||
use crate::statements::alter::{
|
||||
AddColumn, AlterDatabase, AlterDatabaseOperation, AlterTable, AlterTableOperation,
|
||||
DropDefaultsOperation, KeyValueOption, SetIndexOperation, UnsetIndexOperation,
|
||||
DropDefaultsOperation, KeyValueOption, SetDefaultsOperation, SetIndexOperation,
|
||||
UnsetIndexOperation,
|
||||
};
|
||||
use crate::statements::statement::Statement;
|
||||
use crate::util::parse_option_string;
|
||||
@@ -156,7 +157,6 @@ impl ParserContext<'_> {
|
||||
.collect();
|
||||
AlterTableOperation::SetTableOptions { options }
|
||||
}
|
||||
Keyword::ALTER => self.parse_alter_columns()?,
|
||||
_ => self.expected(
|
||||
"ADD or DROP or MODIFY or RENAME or SET after ALTER TABLE",
|
||||
self.parser.peek_token(),
|
||||
@@ -169,29 +169,6 @@ impl ParserContext<'_> {
|
||||
Ok(AlterTable::new(table_name, alter_operation))
|
||||
}
|
||||
|
||||
// Parse the following: ALTER TABLE table_name ALTER ...
|
||||
fn parse_alter_columns(&mut self) -> Result<AlterTableOperation> {
|
||||
let _ = self.parser.next_token();
|
||||
let _ = self.parser.next_token();
|
||||
let ts = self.parser.next_token();
|
||||
match ts.token {
|
||||
// Parse `DROP DEFAULT`: ALTER TABLE `table_name` ALTER `a` DROP DEFAULT, ALTER `b` DROP DEFAULT, ...
|
||||
Token::Word(w) if w.keyword == Keyword::DROP => {
|
||||
let ts = self.parser.peek_token();
|
||||
match ts.token {
|
||||
Token::Word(w) if w.keyword == Keyword::DEFAULT => {
|
||||
self.parser.prev_token();
|
||||
self.parser.prev_token();
|
||||
self.parser.prev_token();
|
||||
self.parse_alter_table_drop_default()
|
||||
}
|
||||
_ => self.expected("DEFAULT is expecting after DROP", ts),
|
||||
}
|
||||
}
|
||||
_ => self.expected("DROP after ALTER COLUMN", ts),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_alter_table_unset(&mut self) -> Result<AlterTableOperation> {
|
||||
let _ = self.parser.next_token();
|
||||
let keys = self
|
||||
@@ -222,12 +199,43 @@ impl ParserContext<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_alter_table_drop_default(&mut self) -> Result<AlterTableOperation> {
|
||||
let columns = self
|
||||
.parser
|
||||
.parse_comma_separated(parse_alter_column_drop_default)
|
||||
.context(error::SyntaxSnafu)?;
|
||||
Ok(AlterTableOperation::DropDefaults { columns })
|
||||
fn parse_alter_table_drop_default(
|
||||
&mut self,
|
||||
column_name: Ident,
|
||||
) -> Result<AlterTableOperation> {
|
||||
let drop_default = DropDefaultsOperation(column_name);
|
||||
if self.parser.consume_token(&Token::Comma) {
|
||||
let mut columns = self
|
||||
.parser
|
||||
.parse_comma_separated(parse_alter_column_drop_default)
|
||||
.context(error::SyntaxSnafu)?;
|
||||
columns.insert(0, drop_default);
|
||||
Ok(AlterTableOperation::DropDefaults { columns })
|
||||
} else {
|
||||
Ok(AlterTableOperation::DropDefaults {
|
||||
columns: vec![drop_default],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_alter_table_set_default(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
|
||||
let default_constraint = self.parser.parse_expr().context(error::SyntaxSnafu)?;
|
||||
let set_default = SetDefaultsOperation {
|
||||
column_name,
|
||||
default_constraint,
|
||||
};
|
||||
if self.parser.consume_token(&Token::Comma) {
|
||||
let mut defaults = self
|
||||
.parser
|
||||
.parse_comma_separated(parse_alter_column_set_default)
|
||||
.context(error::SyntaxSnafu)?;
|
||||
defaults.insert(0, set_default);
|
||||
Ok(AlterTableOperation::SetDefaults { defaults })
|
||||
} else {
|
||||
Ok(AlterTableOperation::SetDefaults {
|
||||
defaults: vec![set_default],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_alter_table_modify(&mut self) -> Result<AlterTableOperation> {
|
||||
@@ -248,7 +256,23 @@ impl ParserContext<'_> {
|
||||
} else if w.keyword == Keyword::SET {
|
||||
// consume the current token.
|
||||
self.parser.next_token();
|
||||
self.parse_alter_column_set_index(column_name)
|
||||
if let Token::Word(w) = self.parser.peek_token().token
|
||||
&& matches!(w.keyword, Keyword::DEFAULT)
|
||||
{
|
||||
self.parser
|
||||
.expect_keyword(Keyword::DEFAULT)
|
||||
.context(error::SyntaxSnafu)?;
|
||||
self.parse_alter_table_set_default(column_name)
|
||||
} else {
|
||||
self.parse_alter_column_set_index(column_name)
|
||||
}
|
||||
} else if w.keyword == Keyword::DROP {
|
||||
// consume the current token.
|
||||
self.parser.next_token();
|
||||
self.parser
|
||||
.expect_keyword(Keyword::DEFAULT)
|
||||
.context(error::SyntaxSnafu)?;
|
||||
self.parse_alter_table_drop_default(column_name)
|
||||
} else {
|
||||
let data_type = self.parser.parse_data_type().context(error::SyntaxSnafu)?;
|
||||
Ok(AlterTableOperation::ModifyColumnType {
|
||||
@@ -348,9 +372,9 @@ impl ParserContext<'_> {
|
||||
.context(error::SyntaxSnafu)?;
|
||||
self.parse_alter_column_skipping(column_name)
|
||||
}
|
||||
_ => self.expected(
|
||||
t => self.expected(
|
||||
format!("{:?} OR INVERTED OR SKIPPING INDEX", Keyword::FULLTEXT).as_str(),
|
||||
self.parser.peek_token(),
|
||||
t,
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -420,17 +444,44 @@ impl ParserContext<'_> {
|
||||
fn parse_alter_column_drop_default(
|
||||
parser: &mut Parser,
|
||||
) -> std::result::Result<DropDefaultsOperation, ParserError> {
|
||||
parser.next_token();
|
||||
parser.expect_keywords(&[Keyword::MODIFY, Keyword::COLUMN])?;
|
||||
let column_name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
|
||||
if parser.parse_keywords(&[Keyword::DROP, Keyword::DEFAULT]) {
|
||||
Ok(DropDefaultsOperation(column_name))
|
||||
} else {
|
||||
let not_drop = parser.peek_token();
|
||||
parser.next_token();
|
||||
let not_default = parser.peek_token();
|
||||
Err(ParserError::ParserError(format!(
|
||||
"Unexpected keyword, expect DROP DEFAULT, got: `{not_drop} {not_default}`"
|
||||
)))
|
||||
let t = parser.next_token();
|
||||
match t.token {
|
||||
Token::Word(w) if w.keyword == Keyword::DROP => {
|
||||
parser.expect_keyword(Keyword::DEFAULT)?;
|
||||
Ok(DropDefaultsOperation(column_name))
|
||||
}
|
||||
_ => Err(ParserError::ParserError(format!(
|
||||
"Unexpected keyword, expect DROP, got: `{t}`"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_alter_column_set_default(
|
||||
parser: &mut Parser,
|
||||
) -> std::result::Result<SetDefaultsOperation, ParserError> {
|
||||
parser.expect_keywords(&[Keyword::MODIFY, Keyword::COLUMN])?;
|
||||
let column_name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
|
||||
let t = parser.next_token();
|
||||
match t.token {
|
||||
Token::Word(w) if w.keyword == Keyword::SET => {
|
||||
parser.expect_keyword(Keyword::DEFAULT)?;
|
||||
if let Ok(default_constraint) = parser.parse_expr() {
|
||||
Ok(SetDefaultsOperation {
|
||||
column_name,
|
||||
default_constraint,
|
||||
})
|
||||
} else {
|
||||
Err(ParserError::ParserError(format!(
|
||||
"Invalid default value after SET DEFAULT, got: `{}`",
|
||||
parser.peek_token()
|
||||
)))
|
||||
}
|
||||
}
|
||||
_ => Err(ParserError::ParserError(format!(
|
||||
"Unexpected keyword, expect SET, got: `{t}`"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,7 +1234,7 @@ mod tests {
|
||||
for col in columns {
|
||||
let sql = col
|
||||
.iter()
|
||||
.map(|x| format!("ALTER {x} DROP DEFAULT"))
|
||||
.map(|x| format!("MODIFY COLUMN {x} DROP DEFAULT"))
|
||||
.collect::<Vec<String>>()
|
||||
.join(",");
|
||||
let sql = format!("ALTER TABLE test_table {sql}");
|
||||
@@ -1214,4 +1265,73 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_alter_set_default() {
|
||||
let columns = vec![vec!["a"], vec!["a", "b"], vec!["a", "b", "c"]];
|
||||
for col in columns {
|
||||
let sql = col
|
||||
.iter()
|
||||
.map(|x| format!("MODIFY COLUMN {x} SET DEFAULT 100"))
|
||||
.collect::<Vec<String>>()
|
||||
.join(",");
|
||||
let sql = format!("ALTER TABLE test_table {sql}");
|
||||
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 { .. });
|
||||
match statement {
|
||||
Statement::AlterTable(alter_table) => {
|
||||
assert_eq!("test_table", alter_table.table_name().0[0].value);
|
||||
let alter_operation = alter_table.alter_operation();
|
||||
match alter_operation {
|
||||
AlterTableOperation::SetDefaults { defaults } => {
|
||||
assert_eq!(col.len(), defaults.len());
|
||||
for i in 0..defaults.len() {
|
||||
assert_eq!(col[i], defaults[i].column_name.to_string());
|
||||
assert_eq!(
|
||||
"100".to_string(),
|
||||
defaults[i].default_constraint.to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_alter_set_default_invalid() {
|
||||
let sql = "ALTER TABLE test_table MODIFY COLUMN a SET 100;";
|
||||
let result =
|
||||
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
|
||||
.unwrap_err();
|
||||
let err = result.output_msg();
|
||||
assert_eq!(err, "Invalid SQL syntax: sql parser error: Expected FULLTEXT OR INVERTED OR SKIPPING INDEX, found: 100");
|
||||
|
||||
let sql = "ALTER TABLE test_table MODIFY COLUMN a SET DEFAULT 100, b SET DEFAULT 200";
|
||||
let result =
|
||||
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
|
||||
.unwrap_err();
|
||||
let err = result.output_msg();
|
||||
assert_eq!(err, "Invalid SQL syntax: sql parser error: Expected: MODIFY, found: b at Line: 1, Column: 57");
|
||||
|
||||
let sql = "ALTER TABLE test_table MODIFY COLUMN a SET DEFAULT 100, MODIFY COLUMN b DROP DEFAULT 200";
|
||||
let result =
|
||||
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
|
||||
.unwrap_err();
|
||||
let err = result.output_msg();
|
||||
assert_eq!(
|
||||
err,
|
||||
"Invalid SQL syntax: sql parser error: Unexpected keyword, expect SET, got: `DROP`"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use common_query::AddColumnLocation;
|
||||
use datatypes::schema::{FulltextOptions, SkippingIndexOptions};
|
||||
use itertools::Itertools;
|
||||
use serde::Serialize;
|
||||
use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName, TableConstraint};
|
||||
use sqlparser::ast::{ColumnDef, DataType, Expr, Ident, ObjectName, TableConstraint};
|
||||
use sqlparser_derive::{Visit, VisitMut};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
|
||||
@@ -95,12 +95,22 @@ pub enum AlterTableOperation {
|
||||
DropDefaults {
|
||||
columns: Vec<DropDefaultsOperation>,
|
||||
},
|
||||
/// `ALTER <column_name> SET DEFAULT <default_value>`
|
||||
SetDefaults {
|
||||
defaults: Vec<SetDefaultsOperation>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
|
||||
/// `ALTER <column_name> DROP DEFAULT`
|
||||
pub struct DropDefaultsOperation(pub Ident);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
|
||||
pub struct SetDefaultsOperation {
|
||||
pub column_name: Ident,
|
||||
pub default_constraint: Expr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut, Serialize)]
|
||||
pub enum SetIndexOperation {
|
||||
/// `MODIFY COLUMN <column_name> SET FULLTEXT INDEX [WITH <options>]`
|
||||
@@ -214,10 +224,22 @@ impl Display for AlterTableOperation {
|
||||
AlterTableOperation::DropDefaults { columns } => {
|
||||
let columns = columns
|
||||
.iter()
|
||||
.map(|column| format!("ALTER {} DROP DEFAULT", column.0))
|
||||
.map(|column| format!("MODIFY COLUMN {} DROP DEFAULT", column.0))
|
||||
.join(", ");
|
||||
write!(f, "{columns}")
|
||||
}
|
||||
AlterTableOperation::SetDefaults { defaults } => {
|
||||
let defaults = defaults
|
||||
.iter()
|
||||
.map(|column| {
|
||||
format!(
|
||||
"MODIFY COLUMN {} SET DEFAULT {}",
|
||||
column.column_name, column.default_constraint
|
||||
)
|
||||
})
|
||||
.join(", ");
|
||||
write!(f, "{defaults}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,7 +524,7 @@ ALTER TABLE monitor MODIFY COLUMN a SET INVERTED INDEX"#,
|
||||
}
|
||||
}
|
||||
|
||||
let sql = "ALTER TABLE monitor ALTER a DROP DEFAULT";
|
||||
let sql = "ALTER TABLE monitor MODIFY COLUMN a DROP DEFAULT";
|
||||
let stmts =
|
||||
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
|
||||
.unwrap();
|
||||
@@ -514,7 +536,28 @@ ALTER TABLE monitor MODIFY COLUMN a SET INVERTED INDEX"#,
|
||||
let new_sql = format!("\n{}", set);
|
||||
assert_eq!(
|
||||
r#"
|
||||
ALTER TABLE monitor ALTER a DROP DEFAULT"#,
|
||||
ALTER TABLE monitor MODIFY COLUMN a DROP DEFAULT"#,
|
||||
&new_sql
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
let sql = "ALTER TABLE monitor MODIFY COLUMN a SET DEFAULT 'default_for_a'";
|
||||
let stmts =
|
||||
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
|
||||
.unwrap();
|
||||
assert_eq!(1, stmts.len());
|
||||
assert_matches!(&stmts[0], Statement::AlterTable { .. });
|
||||
|
||||
match &stmts[0] {
|
||||
Statement::AlterTable(set) => {
|
||||
let new_sql = format!("\n{}", set);
|
||||
assert_eq!(
|
||||
r#"
|
||||
ALTER TABLE monitor MODIFY COLUMN a SET DEFAULT 'default_for_a'"#,
|
||||
&new_sql
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ common-error.workspace = true
|
||||
common-grpc.workspace = true
|
||||
common-macro.workspace = true
|
||||
common-recordbatch.workspace = true
|
||||
common-sql.workspace = true
|
||||
common-time.workspace = true
|
||||
common-wal.workspace = true
|
||||
datafusion-expr.workspace = true
|
||||
@@ -30,6 +31,7 @@ prost.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
snafu.workspace = true
|
||||
sqlparser.workspace = true
|
||||
strum.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
|
||||
@@ -592,6 +592,7 @@ impl RegionMetadataBuilder {
|
||||
AlterKind::DropDefaults { names } => {
|
||||
self.drop_defaults(names)?;
|
||||
}
|
||||
AlterKind::SetDefaults { columns } => self.set_defaults(&columns)?,
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
@@ -872,6 +873,38 @@ impl RegionMetadataBuilder {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_defaults(&mut self, set_defaults: &[crate::region_request::SetDefault]) -> Result<()> {
|
||||
for set_default in set_defaults.iter() {
|
||||
let meta = self
|
||||
.column_metadatas
|
||||
.iter_mut()
|
||||
.find(|col| col.column_schema.name == set_default.name);
|
||||
if let Some(meta) = meta {
|
||||
let default_constraint = common_sql::convert::deserialize_default_constraint(
|
||||
set_default.default_constraint.as_slice(),
|
||||
&meta.column_schema.name,
|
||||
&meta.column_schema.data_type,
|
||||
)
|
||||
.context(SqlCommonSnafu)?;
|
||||
|
||||
meta.column_schema = meta
|
||||
.column_schema
|
||||
.clone()
|
||||
.with_default_constraint(default_constraint)
|
||||
.with_context(|_| CastDefaultValueSnafu {
|
||||
reason: format!("Failed to set default : {set_default:?}"),
|
||||
})?;
|
||||
} else {
|
||||
return InvalidRegionRequestSnafu {
|
||||
region_id: self.region_id,
|
||||
err: format!("column {} not found", set_default.name),
|
||||
}
|
||||
.fail();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields skipped in serialization.
|
||||
@@ -1089,11 +1122,21 @@ pub enum MetadataError {
|
||||
#[snafu(source)]
|
||||
error: datatypes::error::Error,
|
||||
},
|
||||
|
||||
#[snafu(display("Sql common error"))]
|
||||
SqlCommon {
|
||||
source: common_sql::error::Error,
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
}
|
||||
|
||||
impl ErrorExt for MetadataError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::InvalidArguments
|
||||
match self {
|
||||
Self::SqlCommon { source, .. } => source.status_code(),
|
||||
_ => StatusCode::InvalidArguments,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
|
||||
@@ -562,6 +562,16 @@ pub enum AlterKind {
|
||||
/// Name of columns to drop.
|
||||
names: Vec<String>,
|
||||
},
|
||||
/// Set column default value.
|
||||
SetDefaults {
|
||||
/// Columns to change.
|
||||
columns: Vec<SetDefault>,
|
||||
},
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct SetDefault {
|
||||
pub name: String,
|
||||
pub default_constraint: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
@@ -738,7 +748,12 @@ impl AlterKind {
|
||||
AlterKind::DropDefaults { names } => {
|
||||
names
|
||||
.iter()
|
||||
.try_for_each(|name| Self::validate_column_to_drop(name, metadata))?;
|
||||
.try_for_each(|name| Self::validate_column_existence(name, metadata))?;
|
||||
}
|
||||
AlterKind::SetDefaults { columns } => {
|
||||
columns
|
||||
.iter()
|
||||
.try_for_each(|col| Self::validate_column_existence(&col.name, metadata))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -772,6 +787,9 @@ impl AlterKind {
|
||||
AlterKind::DropDefaults { names } => names
|
||||
.iter()
|
||||
.any(|name| metadata.column_by_name(name).is_some()),
|
||||
AlterKind::SetDefaults { columns } => columns
|
||||
.iter()
|
||||
.any(|x| metadata.column_by_name(&x.name).is_some()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,6 +836,18 @@ impl AlterKind {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns an error if the column isn't exist.
|
||||
fn validate_column_existence(column_name: &String, metadata: &RegionMetadata) -> Result<()> {
|
||||
metadata
|
||||
.column_by_name(column_name)
|
||||
.context(InvalidRegionRequestSnafu {
|
||||
region_id: metadata.region_id,
|
||||
err: format!("column {} not found", column_name),
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<alter_request::Kind> for AlterKind {
|
||||
@@ -882,6 +912,18 @@ impl TryFrom<alter_request::Kind> for AlterKind {
|
||||
alter_request::Kind::DropDefaults(x) => AlterKind::DropDefaults {
|
||||
names: x.drop_defaults.into_iter().map(|x| x.column_name).collect(),
|
||||
},
|
||||
alter_request::Kind::SetDefaults(x) => AlterKind::SetDefaults {
|
||||
columns: x
|
||||
.set_defaults
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
Ok(SetDefault {
|
||||
name: x.column_name,
|
||||
default_constraint: x.default_constraint.clone(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
},
|
||||
};
|
||||
|
||||
Ok(alter_kind)
|
||||
|
||||
@@ -21,6 +21,7 @@ common-error.workspace = true
|
||||
common-macro.workspace = true
|
||||
common-query.workspace = true
|
||||
common-recordbatch.workspace = true
|
||||
common-sql.workspace = true
|
||||
common-telemetry.workspace = true
|
||||
common-time.workspace = true
|
||||
datafusion.workspace = true
|
||||
@@ -38,6 +39,7 @@ once_cell.workspace = true
|
||||
paste.workspace = true
|
||||
serde.workspace = true
|
||||
snafu.workspace = true
|
||||
sqlparser.workspace = true
|
||||
store-api.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
|
||||
@@ -195,6 +195,13 @@ pub enum Error {
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
|
||||
#[snafu(display("Sql common error"))]
|
||||
SqlCommon {
|
||||
source: common_sql::error::Error,
|
||||
#[snafu(implicit)]
|
||||
location: Location,
|
||||
},
|
||||
}
|
||||
|
||||
impl ErrorExt for Error {
|
||||
@@ -211,6 +218,7 @@ impl ErrorExt for Error {
|
||||
Error::CastDefaultValue { source, .. } => source.status_code(),
|
||||
Error::TablesRecordBatch { .. } => StatusCode::Unexpected,
|
||||
Error::ColumnExists { .. } => StatusCode::TableColumnExists,
|
||||
Error::SqlCommon { source, .. } => source.status_code(),
|
||||
Error::SchemaBuild { source, .. } | Error::SetFulltextOptions { source, .. } => {
|
||||
source.status_code()
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ use store_api::storage::{ColumnDescriptor, ColumnDescriptorBuilder, ColumnId, Re
|
||||
|
||||
use crate::error::{self, Result};
|
||||
use crate::requests::{
|
||||
AddColumnRequest, AlterKind, ModifyColumnTypeRequest, SetIndexOption, TableOptions,
|
||||
UnsetIndexOption,
|
||||
AddColumnRequest, AlterKind, ModifyColumnTypeRequest, SetDefaultRequest, SetIndexOption,
|
||||
TableOptions, UnsetIndexOption,
|
||||
};
|
||||
use crate::table_reference::TableReference;
|
||||
|
||||
@@ -246,6 +246,7 @@ impl TableMeta {
|
||||
AlterKind::SetIndexes { options } => self.set_indexes(table_name, options),
|
||||
AlterKind::UnsetIndexes { options } => self.unset_indexes(table_name, options),
|
||||
AlterKind::DropDefaults { names } => self.drop_defaults(table_name, names),
|
||||
AlterKind::SetDefaults { defaults } => self.set_defaults(table_name, defaults),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -991,6 +992,49 @@ impl TableMeta {
|
||||
|
||||
Ok(meta_builder)
|
||||
}
|
||||
|
||||
fn set_defaults(
|
||||
&self,
|
||||
table_name: &str,
|
||||
set_defaults: &[SetDefaultRequest],
|
||||
) -> Result<TableMetaBuilder> {
|
||||
let table_schema = &self.schema;
|
||||
let mut meta_builder = self.new_meta_builder();
|
||||
let mut columns = Vec::with_capacity(table_schema.num_columns());
|
||||
for column_schema in table_schema.column_schemas() {
|
||||
if let Some(set_default) = set_defaults
|
||||
.iter()
|
||||
.find(|s| s.column_name == column_schema.name)
|
||||
{
|
||||
let new_column_schema = column_schema.clone();
|
||||
let new_column_schema = new_column_schema
|
||||
.with_default_constraint(set_default.default_constraint.clone())
|
||||
.with_context(|_| error::SchemaBuildSnafu {
|
||||
msg: format!("Table {table_name} cannot set default values"),
|
||||
})?;
|
||||
columns.push(new_column_schema);
|
||||
} else {
|
||||
columns.push(column_schema.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut builder = SchemaBuilder::try_from_columns(columns)
|
||||
.with_context(|_| error::SchemaBuildSnafu {
|
||||
msg: format!("Failed to convert column schemas into schema for table {table_name}"),
|
||||
})?
|
||||
// Also bump the schema version.
|
||||
.version(table_schema.version() + 1);
|
||||
for (k, v) in table_schema.metadata().iter() {
|
||||
builder = builder.add_metadata(k, v);
|
||||
}
|
||||
let new_schema = builder.build().with_context(|_| error::SchemaBuildSnafu {
|
||||
msg: format!("Table {table_name} cannot set default values"),
|
||||
})?;
|
||||
|
||||
let _ = meta_builder.schema(Arc::new(new_schema));
|
||||
|
||||
Ok(meta_builder)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Builder)]
|
||||
|
||||
@@ -26,7 +26,9 @@ use common_time::range::TimestampRange;
|
||||
use common_time::TimeToLive;
|
||||
use datatypes::data_type::ConcreteDataType;
|
||||
use datatypes::prelude::VectorRef;
|
||||
use datatypes::schema::{ColumnSchema, FulltextOptions, SkippingIndexOptions};
|
||||
use datatypes::schema::{
|
||||
ColumnDefaultConstraint, ColumnSchema, FulltextOptions, SkippingIndexOptions,
|
||||
};
|
||||
use greptime_proto::v1::region::compact_request;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use store_api::metric_engine_consts::{
|
||||
@@ -289,6 +291,15 @@ pub enum AlterKind {
|
||||
DropDefaults {
|
||||
names: Vec<String>,
|
||||
},
|
||||
SetDefaults {
|
||||
defaults: Vec<SetDefaultRequest>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SetDefaultRequest {
|
||||
pub column_name: String,
|
||||
pub default_constraint: Option<ColumnDefaultConstraint>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -51,11 +51,11 @@ SELECT * FROM test1;
|
||||
| 1 | 2024-01-30T12:00:00 | 100 | 200 | 2024-01-30T00:00:00 | 300 |
|
||||
+---+---------------------+-----+-----+---------------------+-----+
|
||||
|
||||
ALTER TABLE test1 ALTER k DROP DEFAULT;
|
||||
ALTER TABLE test1 MODIFY COLUMN k DROP DEFAULT;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
ALTER TABLE test1 ALTER l DROP DEFAULT, ALTER m DROP DEFAULT, ALTER n DROP DEFAULT;
|
||||
ALTER TABLE test1 MODIFY COLUMN l DROP DEFAULT, MODIFY COLUMN m DROP DEFAULT, MODIFY COLUMN n DROP DEFAULT;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
@@ -103,7 +103,7 @@ SELECT * FROM test1;
|
||||
| 1 | 2024-01-30T12:00:00 | | | | | 400 |
|
||||
+---+---------------------+---+---+---+---+-----+
|
||||
|
||||
ALTER TABLE test1 ALTER o DROP DEFAULT;
|
||||
ALTER TABLE test1 MODIFY COLUMN o DROP DEFAULT;
|
||||
|
||||
Error: 1004(InvalidArguments), Invalid alter table(test1) request: column o is not nullable and `default` cannot be dropped
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ INSERT INTO test1 VALUES (1, '2024-01-30 12:00:00', DEFAULT, DEFAULT, DEFAULT, D
|
||||
|
||||
SELECT * FROM test1;
|
||||
|
||||
ALTER TABLE test1 ALTER k DROP DEFAULT;
|
||||
ALTER TABLE test1 ALTER l DROP DEFAULT, ALTER m DROP DEFAULT, ALTER n DROP DEFAULT;
|
||||
ALTER TABLE test1 MODIFY COLUMN k DROP DEFAULT;
|
||||
ALTER TABLE test1 MODIFY COLUMN l DROP DEFAULT, MODIFY COLUMN m DROP DEFAULT, MODIFY COLUMN n DROP DEFAULT;
|
||||
|
||||
SHOW CREATE TABLE test1;
|
||||
|
||||
@@ -25,6 +25,6 @@ SELECT * FROM test1;
|
||||
ALTER TABLE test1 ADD COLUMN o INTEGER NOT NULL DEFAULT 400;
|
||||
SELECT * FROM test1;
|
||||
|
||||
ALTER TABLE test1 ALTER o DROP DEFAULT;
|
||||
ALTER TABLE test1 MODIFY COLUMN o DROP DEFAULT;
|
||||
|
||||
DROP TABLE test1;
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
--- alter table to add new column with default timestamp values aware of session timezone test ---
|
||||
CREATE TABLE test1 (i INTEGER, j TIMESTAMP time index, PRIMARY KEY(i));
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
ALTER TABLE test1 ADD COLUMN k INTEGER DEFAULT 100;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
ALTER TABLE test1 ADD COLUMN l INTEGER DEFAULT 200;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
ALTER TABLE test1 ADD COLUMN m TIMESTAMP DEFAULT '2024-01-30 00:00:00';
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
ALTER TABLE test1 ADD COLUMN n INTEGER DEFAULT 300;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
--- set default value for k, l, n, n ---
|
||||
ALTER TABLE test1 MODIFY COLUMN k SET DEFAULT 101;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
ALTER TABLE test1 MODIFY COLUMN l SET DEFAULT 201, MODIFY COLUMN m SET DEFAULT '2024-01-30 00:00:00', MODIFY COLUMN n SET DEFAULT 300;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
SHOW CREATE TABLE test1;
|
||||
|
||||
+-------+-------------------------------------------------------------+
|
||||
| Table | Create Table |
|
||||
+-------+-------------------------------------------------------------+
|
||||
| test1 | CREATE TABLE IF NOT EXISTS "test1" ( |
|
||||
| | "i" INT NULL, |
|
||||
| | "j" TIMESTAMP(3) NOT NULL, |
|
||||
| | "k" INT NULL DEFAULT 101, |
|
||||
| | "l" INT NULL DEFAULT 201, |
|
||||
| | "m" TIMESTAMP(3) NULL DEFAULT '2024-01-30 00:00:00+0000', |
|
||||
| | "n" INT NULL DEFAULT 300, |
|
||||
| | TIME INDEX ("j"), |
|
||||
| | PRIMARY KEY ("i") |
|
||||
| | ) |
|
||||
| | |
|
||||
| | ENGINE=mito |
|
||||
| | |
|
||||
+-------+-------------------------------------------------------------+
|
||||
|
||||
INSERT INTO test1 VALUES (1, '2024-01-30 12:00:00', DEFAULT, DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
Affected Rows: 1
|
||||
|
||||
SELECT * FROM test1;
|
||||
|
||||
+---+---------------------+-----+-----+---------------------+-----+
|
||||
| i | j | k | l | m | n |
|
||||
+---+---------------------+-----+-----+---------------------+-----+
|
||||
| 1 | 2024-01-30T12:00:00 | 101 | 201 | 2024-01-30T00:00:00 | 300 |
|
||||
+---+---------------------+-----+-----+---------------------+-----+
|
||||
|
||||
--- SET `CURRENT_TIMESTAMP` as default
|
||||
ALTER TABLE test1 MODIFY COLUMN m SET DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
SHOW CREATE TABLE test1;
|
||||
|
||||
+-------+------------------------------------------------------+
|
||||
| Table | Create Table |
|
||||
+-------+------------------------------------------------------+
|
||||
| test1 | CREATE TABLE IF NOT EXISTS "test1" ( |
|
||||
| | "i" INT NULL, |
|
||||
| | "j" TIMESTAMP(3) NOT NULL, |
|
||||
| | "k" INT NULL DEFAULT 101, |
|
||||
| | "l" INT NULL DEFAULT 201, |
|
||||
| | "m" TIMESTAMP(3) NULL DEFAULT current_timestamp(), |
|
||||
| | "n" INT NULL DEFAULT 300, |
|
||||
| | TIME INDEX ("j"), |
|
||||
| | PRIMARY KEY ("i") |
|
||||
| | ) |
|
||||
| | |
|
||||
| | ENGINE=mito |
|
||||
| | |
|
||||
+-------+------------------------------------------------------+
|
||||
|
||||
ALTER TABLE test1 ADD COLUMN o INTEGER NOT NULL DEFAULT 400;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
ALTER TABLE test1 MODIFY COLUMN o SET DEFAULT 401;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
SHOW CREATE TABLE test1;
|
||||
|
||||
+-------+------------------------------------------------------+
|
||||
| Table | Create Table |
|
||||
+-------+------------------------------------------------------+
|
||||
| test1 | CREATE TABLE IF NOT EXISTS "test1" ( |
|
||||
| | "i" INT NULL, |
|
||||
| | "j" TIMESTAMP(3) NOT NULL, |
|
||||
| | "k" INT NULL DEFAULT 101, |
|
||||
| | "l" INT NULL DEFAULT 201, |
|
||||
| | "m" TIMESTAMP(3) NULL DEFAULT current_timestamp(), |
|
||||
| | "n" INT NULL DEFAULT 300, |
|
||||
| | "o" INT NOT NULL DEFAULT 401, |
|
||||
| | TIME INDEX ("j"), |
|
||||
| | PRIMARY KEY ("i") |
|
||||
| | ) |
|
||||
| | |
|
||||
| | ENGINE=mito |
|
||||
| | |
|
||||
+-------+------------------------------------------------------+
|
||||
|
||||
ALTER TABLE test1 MODIFY COLUMN o SET DEFAULT "not allow";
|
||||
|
||||
Error: 1001(Unsupported), Unsupported expr in default constraint: "not allow" for column: o
|
||||
|
||||
ALTER TABLE test1 MODIFY COLUMN o SET DEFAULT NULL;
|
||||
|
||||
Error: 1004(InvalidArguments), Default value should not be null for non null column
|
||||
|
||||
DROP TABLE test1;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
--- alter table to add new column with default timestamp values aware of session timezone test ---
|
||||
|
||||
CREATE TABLE test1 (i INTEGER, j TIMESTAMP time index, PRIMARY KEY(i));
|
||||
|
||||
|
||||
ALTER TABLE test1 ADD COLUMN k INTEGER DEFAULT 100;
|
||||
ALTER TABLE test1 ADD COLUMN l INTEGER DEFAULT 200;
|
||||
ALTER TABLE test1 ADD COLUMN m TIMESTAMP DEFAULT '2024-01-30 00:00:00';
|
||||
ALTER TABLE test1 ADD COLUMN n INTEGER DEFAULT 300;
|
||||
|
||||
--- set default value for k, l, n, n ---
|
||||
ALTER TABLE test1 MODIFY COLUMN k SET DEFAULT 101;
|
||||
ALTER TABLE test1 MODIFY COLUMN l SET DEFAULT 201, MODIFY COLUMN m SET DEFAULT '2024-01-30 00:00:00', MODIFY COLUMN n SET DEFAULT 300;
|
||||
|
||||
SHOW CREATE TABLE test1;
|
||||
|
||||
INSERT INTO test1 VALUES (1, '2024-01-30 12:00:00', DEFAULT, DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
SELECT * FROM test1;
|
||||
|
||||
--- SET `CURRENT_TIMESTAMP` as default
|
||||
ALTER TABLE test1 MODIFY COLUMN m SET DEFAULT CURRENT_TIMESTAMP;
|
||||
SHOW CREATE TABLE test1;
|
||||
|
||||
ALTER TABLE test1 ADD COLUMN o INTEGER NOT NULL DEFAULT 400;
|
||||
|
||||
ALTER TABLE test1 MODIFY COLUMN o SET DEFAULT 401;
|
||||
|
||||
SHOW CREATE TABLE test1;
|
||||
|
||||
ALTER TABLE test1 MODIFY COLUMN o SET DEFAULT "not allow";
|
||||
|
||||
ALTER TABLE test1 MODIFY COLUMN o SET DEFAULT NULL;
|
||||
|
||||
DROP TABLE test1;
|
||||
@@ -54,7 +54,7 @@ show create table t3;
|
||||
|
||||
create table t4 (ts timestamp time index default now);
|
||||
|
||||
Error: 1001(Unsupported), Unsupported expr in default constraint: Identifier(Ident { value: "now", quote_style: None, span: Span(Location(1,50)..Location(1,53)) }) for column: ts
|
||||
Error: 1001(Unsupported), Unsupported expr in default constraint: now for column: ts
|
||||
|
||||
drop table t1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user